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
/**
* Copyright 2018 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 java.math.BigDecimal;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import org.redisson.api.MapOptions;
import org.redisson.api.RCountDownLatch;
import org.redisson.api.MapOptions.WriteMode;
import org.redisson.api.RFuture;
import org.redisson.api.RLock;
import org.redisson.api.RMap;
import org.redisson.api.RPermitExpirableSemaphore;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
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.RedisCommand.ValueType;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.client.protocol.convertor.NumberConvertor;
import org.redisson.client.protocol.decoder.MapScanResult;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.connection.decoder.MapGetAllDecoder;
import org.redisson.mapreduce.RedissonMapReduce;
import org.redisson.misc.Hash;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedissonPromise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.buffer.ByteBuf;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
/**
* 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 AtomicInteger writeBehindCurrentThreads = new AtomicInteger();
final Queue writeBehindTasks;
final RedissonClient redisson;
final MapOptions options;
public RedissonMap(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson, MapOptions options) {
super(commandExecutor, name);
this.redisson = redisson;
this.options = options;
if (options != null && options.getWriteMode() == WriteMode.WRITE_BEHIND) {
writeBehindTasks = new ConcurrentLinkedQueue();
} else {
writeBehindTasks = null;
}
}
public RedissonMap(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson, MapOptions options) {
super(codec, commandExecutor, name);
this.redisson = redisson;
this.options = options;
if (options != null && options.getWriteMode() == WriteMode.WRITE_BEHIND) {
writeBehindTasks = new ConcurrentLinkedQueue();
} else {
writeBehindTasks = null;
}
}
@Override
public RMapReduce mapReduce() {
return new RedissonMapReduce(this, redisson, commandExecutor.getConnectionManager());
}
@Override
public RPermitExpirableSemaphore getPermitExpirableSemaphore(K key) {
String lockName = getLockName(key, "permitexpirablesemaphore");
return new RedissonPermitExpirableSemaphore(commandExecutor, lockName, ((Redisson)redisson).getSemaphorePubSub());
}
@Override
public RSemaphore getSemaphore(K key) {
String lockName = getLockName(key, "semaphore");
return new RedissonSemaphore(commandExecutor, lockName, ((Redisson)redisson).getSemaphorePubSub());
}
@Override
public RCountDownLatch getCountDownLatch(K key) {
String lockName = getLockName(key, "countdownlatch");
return new RedissonCountDownLatch(commandExecutor, lockName);
}
@Override
public RLock getFairLock(K key) {
String lockName = getLockName(key, "fairlock");
return new RedissonFairLock(commandExecutor, lockName);
}
@Override
public RLock getLock(K key) {
String lockName = getLockName(key, "lock");
return new RedissonLock(commandExecutor, lockName);
}
@Override
public RReadWriteLock getReadWriteLock(K key) {
String lockName = getLockName(key, "rw_lock");
return new RedissonReadWriteLock(commandExecutor, lockName);
}
public String getLockName(Object key, String suffix) {
ByteBuf keyState = encodeMapKey(key);
try {
return suffixName(getName(key), Hash.hash128toBase64(keyState) + ":" + suffix);
} finally {
keyState.release();
}
}
@Override
public int size() {
return get(sizeAsync());
}
@Override
public RFuture sizeAsync() {
return commandExecutor.readAsync(getName(), codec, RedisCommands.HLEN, getName());
}
@Override
public int valueSize(K key) {
return get(valueSizeAsync(key));
}
@Override
public RFuture valueSizeAsync(K key) {
checkKey(key);
return commandExecutor.readAsync(getName(), codec, RedisCommands.HSTRLEN, getName(key), 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);
return commandExecutor.readAsync(getName(key), codec, RedisCommands.HEXISTS, getName(key), encodeMapKey(key));
}
@Override
public boolean containsValue(Object value) {
return get(containsValueAsync(value));
}
@Override
public RFuture containsValueAsync(Object value) {
checkValue(value);
return commandExecutor.evalReadAsync(getName(), 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(getName()), encodeMapValue(value));
}
@Override
public Map getAll(Set keys) {
return get(getAllAsync(keys));
}
@Override
public RFuture> getAllAsync(final Set keys) {
if (keys.isEmpty()) {
return RedissonPromise.newSucceededFuture(Collections.emptyMap());
}
RFuture> future = getAllOperationAsync(keys);
if (hasNoLoader()) {
return future;
}
final RPromise> result = new RedissonPromise>();
future.addListener(new FutureListener>() {
@Override
public void operationComplete(final Future> mapFuture) throws Exception {
if (!mapFuture.isSuccess()) {
result.tryFailure(mapFuture.cause());
return;
}
if (!mapFuture.getNow().keySet().containsAll(keys)) {
Set newKeys = new HashSet(keys);
newKeys.removeAll(mapFuture.getNow().keySet());
loadAllAsync(newKeys, false, 1, mapFuture.getNow()).addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
result.trySuccess(mapFuture.getNow());
}
});
} else {
result.trySuccess(mapFuture.getNow());
}
}
});
return result;
}
protected boolean hasNoLoader() {
return options == null || options.getLoader() == null;
}
public RFuture> getAllOperationAsync(Set keys) {
List args = new ArrayList(keys.size() + 1);
args.add(getName());
encodeMapKeys(args, keys);
RFuture> future = commandExecutor.readAsync(getName(), codec, new RedisCommand>("HMGET", new MapGetAllDecoder(new ArrayList(keys), 0), ValueType.MAP_VALUE),
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();
RPromise promise = new RedissonPromise();
putAllAsync(batch, iter, counter, batchSize, promise);
return promise;
}
private void putAllAsync(final Map batch, final Iterator> iter,
final AtomicInteger counter, final int batchSize, final RPromise promise) {
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);
future.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
promise.tryFailure(future.cause());
return;
}
putAllAsync(batch, iter, counter, batchSize, promise);
}
});
return;
}
}
if (batch.isEmpty()) {
promise.trySuccess(null);
return;
}
RFuture future = putAllAsync(batch);
future.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
promise.tryFailure(future.cause());
return;
}
promise.trySuccess(null);
}
});
}
@Override
public final RFuture putAllAsync(final Map extends K, ? extends V> map) {
if (map.isEmpty()) {
return RedissonPromise.newSucceededFuture(null);
}
RFuture future = putAllOperationAsync(map);
if (hasNoWriter()) {
return future;
}
MapWriterTask listener = new MapWriterTask() {
@Override
public void execute() {
options.getWriter().writeAll((Map) map);
}
};
return mapWriterFuture(future, listener);
}
protected final RFuture mapWriterFuture(RFuture future, final MapWriterTask listener) {
if (options != null && options.getWriteMode() == WriteMode.WRITE_BEHIND) {
future.addListener(new MapWriteBehindListener(commandExecutor, listener, writeBehindCurrentThreads, writeBehindTasks, options.getWriteBehindThreads()));
return future;
}
final RPromise promise = new RedissonPromise();
future.addListener(new FutureListener() {
@Override
public void operationComplete(final Future future) throws Exception {
if (!future.isSuccess()) {
promise.tryFailure(future.cause());
return;
}
if (listener.condition(future)) {
commandExecutor.getConnectionManager().getExecutor().execute(new Runnable() {
@Override
public void run() {
try {
listener.execute();
} catch (Exception e) {
promise.tryFailure(e);
return;
}
promise.trySuccess(future.getNow());
}
});
} else {
promise.trySuccess(future.getNow());
}
}
});
return promise;
}
protected RFuture putAllOperationAsync(Map extends K, ? extends V> map) {
List params = new ArrayList(map.size()*2 + 1);
params.add(getName());
for (java.util.Map.Entry extends K, ? extends V> t : map.entrySet()) {
checkKey(t.getKey());
checkValue(t.getValue());
params.add(encodeMapKey(t.getKey()));
params.add(encodeMapValue(t.getValue()));
}
RFuture future = commandExecutor.writeAsync(getName(), 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(getName(), codec, RedisCommands.HKEYS, getName());
}
@Override
public Collection readAllValues() {
return get(readAllValuesAsync());
}
@Override
public RFuture> readAllValuesAsync() {
return commandExecutor.readAsync(getName(), codec, RedisCommands.HVALS, getName());
}
@Override
public Set> readAllEntrySet() {
return get(readAllEntrySetAsync());
}
@Override
public RFuture>> readAllEntrySetAsync() {
return commandExecutor.readAsync(getName(), codec, RedisCommands.HGETALL_ENTRY, getName());
}
@Override
public Map readAllMap() {
return get(readAllMapAsync());
}
@Override
public RFuture> readAllMapAsync() {
return commandExecutor.readAsync(getName(), codec, RedisCommands.HGETALL, getName());
}
@Override
public V putIfAbsent(K key, V value) {
return get(putIfAbsentAsync(key, value));
}
@Override
public RFuture putIfAbsentAsync(final K key, final V value) {
checkKey(key);
checkValue(key);
RFuture future = putIfAbsentOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask listener = new MapWriterTask() {
@Override
public void execute() {
options.getWriter().write(key, value);
}
@Override
protected boolean condition(Future future) {
return future.getNow() == null;
}
};
return mapWriterFuture(future, listener);
}
protected boolean hasNoWriter() {
return options == null || options.getWriter() == null;
}
protected RFuture putIfAbsentOperationAsync(K key, V value) {
return commandExecutor.evalWriteAsync(getName(key), 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(getName(key)), encodeMapKey(key), encodeMapValue(value));
}
@Override
public boolean fastPutIfAbsent(K key, V value) {
return get(fastPutIfAbsentAsync(key, value));
}
@Override
public RFuture fastPutIfAbsentAsync(final K key, final V value) {
checkKey(key);
checkValue(value);
RFuture future = fastPutIfAbsentOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask listener = new MapWriterTask() {
@Override
public void execute() {
options.getWriter().write(key, value);
}
@Override
protected boolean condition(Future future) {
return future.getNow();
}
};
return mapWriterFuture(future, listener);
}
protected RFuture fastPutIfAbsentOperationAsync(K key, V value) {
return commandExecutor.writeAsync(getName(key), codec, RedisCommands.HSETNX, getName(key), encodeMapKey(key), encodeMapValue(value));
}
@Override
public boolean remove(Object key, Object value) {
return get(removeAsync(key, value));
}
@Override
public RFuture removeAsync(final Object key, Object value) {
checkKey(key);
checkValue(value);
RFuture future = removeOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask listener = new MapWriterTask() {
@Override
public void execute() {
options.getWriter().delete((K) key);
}
@Override
protected boolean condition(Future future) {
return future.getNow();
}
};
return mapWriterFuture(future, listener);
}
protected RFuture removeOperationAsync(Object key, Object value) {
RFuture future = commandExecutor.evalWriteAsync(getName(key), 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(getName(key)), encodeMapKey(key), encodeMapValue(value));
return future;
}
protected void checkValue(Object value) {
if (value == null) {
throw new NullPointerException("map value can't be null");
}
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
return get(replaceAsync(key, oldValue, newValue));
}
@Override
public RFuture replaceAsync(final K key, V oldValue, final 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 listener = new MapWriterTask() {
@Override
public void execute() {
options.getWriter().write(key, newValue);
}
@Override
protected boolean condition(Future future) {
return future.getNow();
}
};
return mapWriterFuture(future, listener);
}
protected RFuture replaceOperationAsync(K key, V oldValue, V newValue) {
return commandExecutor.evalWriteAsync(getName(key), 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(getName(key)), encodeMapKey(key), encodeMapValue(oldValue), encodeMapValue(newValue));
}
@Override
public V replace(K key, V value) {
return get(replaceAsync(key, value));
}
@Override
public RFuture replaceAsync(final K key, final V value) {
checkKey(key);
checkValue(value);
RFuture future = replaceOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask listener = new MapWriterTask() {
@Override
public void execute() {
options.getWriter().write(key, value);
}
@Override
protected boolean condition(Future future) {
return future.getNow() != null;
}
};
return mapWriterFuture(future, listener);
}
protected RFuture replaceOperationAsync(final K key, final V value) {
return commandExecutor.evalWriteAsync(getName(key), 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(getName(key)), encodeMapKey(key), encodeMapValue(value));
}
@Override
public boolean fastReplace(K key, V value) {
return get(fastReplaceAsync(key, value));
}
@Override
public RFuture fastReplaceAsync(final K key, final V value) {
checkKey(key);
checkValue(value);
RFuture future = fastReplaceOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask listener = new MapWriterTask() {
@Override
public void execute() {
options.getWriter().write(key, value);
}
@Override
protected boolean condition(Future future) {
return future.getNow();
}
};
return mapWriterFuture(future, listener);
}
protected RFuture fastReplaceOperationAsync(final K key, final V value) {
return commandExecutor.evalWriteAsync(getName(key), 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(getName(key)), encodeMapKey(key), encodeMapValue(value));
}
public RFuture getOperationAsync(K key) {
return commandExecutor.readAsync(getName(key), codec, RedisCommands.HGET, getName(key), encodeMapKey(key));
}
@Override
public RFuture getAsync(final K key) {
checkKey(key);
RFuture future = getOperationAsync(key);
if (hasNoLoader()) {
return future;
}
final RPromise result = new RedissonPromise();
future.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
result.tryFailure(future.cause());
return;
}
if (future.getNow() == null) {
loadValue(key, result, false);
} else {
result.trySuccess(future.getNow());
}
}
});
return result;
}
private RFuture externalPutAsync(K key, V value) {
return putAsync(key, value);
}
@Override
public void loadAll(boolean replaceExistingValues, int parallelism) {
get(loadAllAsync(replaceExistingValues, parallelism));
}
@Override
public RFuture loadAllAsync(boolean replaceExistingValues, int parallelism) {
Iterable keys;
try {
keys = options.getLoader().loadAllKeys();
} catch (Exception e) {
log.error("Unable to load keys for map " + getName(), e);
return RedissonPromise.newFailedFuture(e);
}
return loadAllAsync(keys, replaceExistingValues, parallelism, null);
}
@Override
public void loadAll(Set extends K> keys, boolean replaceExistingValues, int parallelism) {
get(loadAllAsync(keys, replaceExistingValues, parallelism));
}
@Override
public RFuture loadAllAsync(final Set extends K> keys, boolean replaceExistingValues, int parallelism) {
return loadAllAsync((Iterable)keys, replaceExistingValues, parallelism, null);
}
private RFuture loadAllAsync(final Iterable extends K> keys, boolean replaceExistingValues, int parallelism, Map loadedEntires) {
if (parallelism < 1) {
throw new IllegalArgumentException("parallelism can't be lower than 1");
}
for (K key : keys) {
checkKey(key);
}
final RPromise result = new RedissonPromise();
final AtomicInteger counter = new AtomicInteger();
try {
final Iterator extends K> iter = keys.iterator();
for (int i = 0; i < parallelism; i++) {
if (!iter.hasNext()) {
if (counter.get() == 0) {
result.trySuccess(null);
}
break;
}
counter.incrementAndGet();
K key = iter.next();
if (replaceExistingValues) {
loadValue(result, counter, iter, key, loadedEntires);
} else {
checkAndLoadValue(result, counter, iter, key, loadedEntires);
}
}
} catch (Exception e) {
log.error("Unable to load keys for map " + getName(), e);
return RedissonPromise.newFailedFuture(e);
}
return result;
}
private void checkAndLoadValue(final RPromise result, final AtomicInteger counter, final Iterator extends K> iter,
final K key, final Map loadedEntires) {
containsKeyAsync(key).addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
result.tryFailure(future.cause());
return;
}
if (!future.getNow()) {
RPromise promise = new RedissonPromise();
promise.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
result.tryFailure(future.cause());
return;
}
if (loadedEntires != null && future.getNow() != null) {
loadedEntires.put(key, future.getNow());
}
checkAndLoadValue(result, counter, iter, loadedEntires);
}
});
loadValue(key, promise, false);
} else {
checkAndLoadValue(result, counter, iter, loadedEntires);
}
}
});
}
private void checkAndLoadValue(RPromise result, AtomicInteger counter, Iterator extends K> iter, Map loadedEntires) {
K key = null;
synchronized (iter) {
if (iter.hasNext()) {
key = iter.next();
}
}
if (key == null) {
if (counter.decrementAndGet() == 0) {
result.trySuccess(null);
}
} else if (!result.isDone()) {
checkAndLoadValue(result, counter, iter, key, loadedEntires);
}
}
private void loadValue(final RPromise result, final AtomicInteger counter, final Iterator extends K> iter,
final K key, final Map loadedEntires) {
RPromise promise = new RedissonPromise();
promise.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
result.tryFailure(future.cause());
return;
}
if (loadedEntires != null && future.getNow() != null) {
loadedEntires.put(key, future.getNow());
}
K key = null;
synchronized (iter) {
if (iter.hasNext()) {
key = iter.next();
}
}
if (key == null) {
if (counter.decrementAndGet() == 0) {
result.trySuccess(null);
}
} else if (!result.isDone()) {
loadValue(result, counter, iter, key, loadedEntires);
}
}
});
loadValue(key, promise, true);
}
@Override
public RFuture putAsync(final K key, final V value) {
checkKey(key);
checkValue(value);
RFuture future = putOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask listener = new MapWriterTask() {
@Override
public void execute() {
options.getWriter().write(key, value);
}
};
return mapWriterFuture(future, listener);
}
protected RFuture putOperationAsync(K key, V value) {
return commandExecutor.evalWriteAsync(getName(key), 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(getName(key)), encodeMapKey(key), encodeMapValue(value));
}
@Override
public RFuture removeAsync(final K key) {
checkKey(key);
RFuture future = removeOperationAsync(key);
if (hasNoWriter()) {
return future;
}
MapWriterTask listener = new MapWriterTask() {
@Override
public void execute() {
options.getWriter().delete(key);
}
};
return mapWriterFuture(future, listener);
}
protected RFuture removeOperationAsync(K key) {
return commandExecutor.evalWriteAsync(getName(key), 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(getName(key)), encodeMapKey(key));
}
@Override
public RFuture fastPutAsync(final K key, final V value) {
checkKey(key);
checkValue(value);
RFuture future = fastPutOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask listener = new MapWriterTask() {
@Override
public void execute() {
options.getWriter().write(key, value);
}
};
return mapWriterFuture(future, listener);
}
protected RFuture fastPutOperationAsync(K key, V value) {
return commandExecutor.writeAsync(getName(key), codec, RedisCommands.HSET, getName(key), encodeMapKey(key), encodeMapValue(value));
}
@Override
public boolean fastPut(K key, V value) {
return get(fastPutAsync(key, value));
}
@Override
public RFuture fastRemoveAsync(final K ... keys) {
if (keys == null) {
throw new NullPointerException();
}
if (keys.length == 0) {
return RedissonPromise.newSucceededFuture(0L);
}
if (hasNoWriter()) {
return fastRemoveOperationAsync(keys);
}
RFuture> future = fastRemoveOperationBatchAsync(keys);
final RPromise result = new RedissonPromise();
future.addListener(new FutureListener>() {
@Override
public void operationComplete(Future> future) throws Exception {
if (!future.isSuccess()) {
result.tryFailure(future.cause());
return;
}
if (future.getNow().isEmpty()) {
result.trySuccess(0L);
return;
}
final List deletedKeys = new ArrayList();
for (int i = 0; i < future.getNow().size(); i++) {
if (future.getNow().get(i) == 1) {
deletedKeys.add(keys[i]);
}
}
if (options.getWriteMode() == WriteMode.WRITE_BEHIND) {
result.trySuccess((long)deletedKeys.size());
MapWriterTask> listener = new MapWriterTask>() {
@Override
public void execute() {
options.getWriter().deleteAll(deletedKeys);
}
};
future.addListener(new MapWriteBehindListener>(commandExecutor, listener, writeBehindCurrentThreads, writeBehindTasks, options.getWriteBehindThreads()));
} else {
commandExecutor.getConnectionManager().getExecutor().execute(new Runnable() {
@Override
public void run() {
options.getWriter().deleteAll(deletedKeys);
result.trySuccess((long)deletedKeys.size());
}
});
}
}
});
return result;
}
protected RFuture> fastRemoveOperationBatchAsync(final K... keys) {
List args = new ArrayList(keys.length);
for (K key : keys) {
args.add(encodeMapKey(key));
}
RFuture> future = commandExecutor.evalWriteAsync(getName(), 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(getName()),
args.toArray());
return future;
}
protected RFuture fastRemoveOperationAsync(K... keys) {
List args = new ArrayList(keys.length + 1);
args.add(getName());
for (K key : keys) {
args.add(encodeMapKey(key));
}
return commandExecutor.writeAsync(getName(), codec, RedisCommands.HDEL, args.toArray());
}
@Override
public long fastRemove(K ... keys) {
return get(fastRemoveAsync(keys));
}
public MapScanResult scanIterator(String name, RedisClient client, long startPos, String pattern, int count) {
RFuture> f = scanIteratorAsync(name, client, startPos, pattern, count);
return get(f);
}
public RFuture> scanIteratorAsync(String name, RedisClient client, long 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(final K key, Number value) {
checkKey(key);
checkValue(value);
final RFuture future = addAndGetOperationAsync(key, value);
if (hasNoWriter()) {
return future;
}
MapWriterTask listener = new MapWriterTask() {
@Override
public void execute() {
options.getWriter().write(key, future.getNow());
}
};
return mapWriterFuture(future, listener);
}
protected RFuture addAndGetOperationAsync(K key, Number value) {
ByteBuf keyState = encodeMapKey(key);
RFuture future = commandExecutor.writeAsync(getName(key), StringCodec.INSTANCE,
new RedisCommand("HINCRBYFLOAT", new NumberConvertor(value.getClass())),
getName(key), 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 RedissonMapIterator(RedissonMap.this, pattern, count) {
@Override
protected K getValue(java.util.Map.Entry entry) {
return (K) entry.getKey();
}
};
}
class KeySet extends AbstractSet {
private final String pattern;
private final int count;
public KeySet(String pattern, int count) {
this.pattern = pattern;
this.count = count;
}
@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;
public Values(String keyPattern, int count) {
this.keyPattern = keyPattern;
this.count = count;
}
@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);
}
private void loadValue(final K key, final RPromise result, final boolean replaceValue) {
final RLock lock = getLock(key);
final long threadId = Thread.currentThread().getId();
lock.lockAsync(threadId).addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
lock.unlockAsync(threadId);
result.tryFailure(future.cause());
return;
}
if (replaceValue) {
loadValue(key, result, lock, threadId);
return;
}
getOperationAsync(key).addListener(new FutureListener() {
@Override
public void operationComplete(Future valueFuture) throws Exception {
if (!valueFuture.isSuccess()) {
lock.unlockAsync(threadId);
result.tryFailure(valueFuture.cause());
return;
}
if (valueFuture.getNow() != null) {
unlock(result, lock, threadId, valueFuture.getNow());
return;
}
loadValue(key, result, lock, threadId);
}
});
}
});
}
private void loadValue(final K key, final RPromise result, final RLock lock,
final long threadId) {
commandExecutor.getConnectionManager().getExecutor().execute(new Runnable() {
@Override
public void run() {
final V value;
try {
value = options.getLoader().load(key);
if (value == null) {
unlock(result, lock, threadId, value);
return;
}
} catch (Exception e) {
log.error("Unable to load value by key " + key + " for map " + getName(), e);
unlock(result, lock, threadId, null);
return;
}
externalPutAsync(key, value).addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
lock.unlockAsync(threadId);
result.tryFailure(future.cause());
return;
}
unlock(result, lock, threadId, value);
}
});
}
});
}
private void unlock(final RPromise result, RLock lock, long threadId,
final V value) {
lock.unlockAsync(threadId).addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
result.tryFailure(future.cause());
return;
}
result.trySuccess(value);
}
});
}
final class EntrySet extends AbstractSet> {
private final String keyPattern;
private final int count;
public EntrySet(String keyPattern, int count) {
this.keyPattern = keyPattern;
this.count = count;
}
public final Iterator> iterator() {
return entryIterator(keyPattern, count);
}
public final 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);
}
public final 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;
}
public final int size() {
if (keyPattern != null) {
int size = 0;
for (Entry val : this) {
size++;
}
return size;
}
return RedissonMap.this.size();
}
public final void clear() {
RedissonMap.this.clear();
}
}
}