All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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 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 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 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 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 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 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 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 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> 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());

                return loadAllAsync(newKeys, false, 1, res)
                        .thenApply(r -> res);
            }
            return CompletableFuture.completedFuture(res);
        });
        return new CompletableFutureWrapper<>(f);
    }

    protected boolean hasNoLoader() {
        return options == null || options.getLoader() == 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 map) {
        get(putAllAsync(map));
    }

    @Override
    public void putAll(Map map, int batchSize) {
        get(putAllAsync(map, batchSize));
    }
    
    @Override
    public RFuture putAllAsync(Map 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 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)) {
                CompletableFuture promise = new CompletableFuture<>();
                commandExecutor.getConnectionManager().getExecutor().execute(() -> {
                    try {
                        if (task instanceof MapWriterTask.Add) {
                            options.getWriter().write(task.getMap());
                        } else {
                            options.getWriter().delete(task.getKeys());
                        }
                    } catch (Exception ex) {
                        promise.completeExceptionally(ex);
                        return;
                    }
                    promise.complete(res);
                });
                return promise;
            }
            return CompletableFuture.completedFuture(res);
        });

        return new CompletableFutureWrapper<>(f);
    }

    protected RFuture putAllOperationAsync(Map map) {
        List params = new ArrayList<>(map.size()*2 + 1);
        params.add(getRawName());
        for (java.util.Map.Entry t : map.entrySet()) {
            checkKey(t.getKey());
            checkValue(t.getValue());

            params.add(encodeMapKey(t.getKey()));
            params.add(encodeMapValue(t.getValue()));
        }

        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;
    }

    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");
        }
    }

    @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 RFuture getAsync(K key) {
        checkKey(key);

        RFuture future = getOperationAsync(key);
        if (hasNoLoader()) {
            return future;
        }

        long threadId = Thread.currentThread().getId();
        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 (options.getLoader() == null) {
            throw new NullPointerException("MapLoader isn't defined");
        }

        Iterable keys;
        try {
            keys = options.getLoader().loadAllKeys();
        } catch (Exception e) {
            log.error("Unable to load keys for map " + getRawName(), e);
            return new CompletableFutureWrapper<>(e);
        }
        return loadAllAsync(keys, replaceExistingValues, parallelism, null);
    }
    
    @Override
    public void loadAll(Set keys, boolean replaceExistingValues, int parallelism) {
        get(loadAllAsync(keys, replaceExistingValues, parallelism));
    }
    
    @Override
    public RFuture loadAllAsync(Set keys, boolean replaceExistingValues, int parallelism) {
        return loadAllAsync(keys, replaceExistingValues, parallelism, null);
    }
    
    protected RFuture loadAllAsync(Iterable keys, boolean replaceExistingValues, int parallelism, Map loadedEntires) {
        if (options.getLoader() == null) {
            throw new NullPointerException("MapLoader isn't defined");
        }

        if (parallelism < 1) {
            throw new IllegalArgumentException("parallelism can't be lower than 1");
        }

        for (K key : keys) {
            checkKey(key);
        }
 
        List> futures = new ArrayList<>();
        try {
            Iterator iter = keys.iterator();
            for (int i = 0; i < parallelism; i++) {
                if (!iter.hasNext()) {
                    break;
                }
                
                K key = iter.next();
                if (replaceExistingValues) {
                    CompletableFuture f = loadValue(iter, key, loadedEntires);
                    futures.add(f);
                } else {
                    CompletableFuture f = checkAndLoadValue(iter, key, loadedEntires);
                    futures.add(f);
                }
            }
        } catch (Exception e) {
            log.error("Unable to load keys for map " + getRawName(), e);
            return new CompletableFutureWrapper<>(e);
        }

        CompletableFuture f = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
        return new CompletableFutureWrapper<>(f);
    }

    private CompletableFuture checkAndLoadValue(Iterator iter, K key, Map loadedEntires) {
        CompletableFuture valuePromise = new CompletableFuture<>();
        valuePromise.whenComplete((r, e) -> {
            if (loadedEntires != null && r != null) {
                loadedEntires.put(key, r);
            }
        });

        CompletableFuture future = containsKeyAsync(key, valuePromise).toCompletableFuture();
        return future.thenCompose(res -> {
            if (!res) {
                CompletableFuture promise = loadValue(key, false);
                return promise.thenCompose(r -> {
                    if (loadedEntires != null && r != null) {
                        loadedEntires.put(key, r);
                    }

                    return checkAndLoadValue(iter, loadedEntires);
                });
            }
            return checkAndLoadValue(iter, loadedEntires);
        });
    }
    
    private CompletableFuture checkAndLoadValue(Iterator iter, Map loadedEntires) {
        K key = null;
        synchronized (iter) {
            if (iter.hasNext()) {
                key = iter.next();
            }
        }
        
        if (key == null) {
            return CompletableFuture.completedFuture(null);
        }
        return checkAndLoadValue(iter, key, loadedEntires);
    }
    
    private CompletableFuture loadValue(Iterator iter, K k, Map loadedEntires) {
        CompletableFuture promise = loadValue(k, true);
        return promise.thenCompose(res -> {
            if (loadedEntires != null && res != null) {
                loadedEntires.put(k, res);
            }

            K key = null;
            synchronized (iter) {
                if (iter.hasNext()) {
                    key = iter.next();
                }
            }

            if (key == null) {
                return CompletableFuture.completedFuture(null);
            }
            return loadValue(iter, key, loadedEntires);
        });
    }
    
    @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 {
                CompletableFuture future = new CompletableFuture<>();
                commandExecutor.getConnectionManager().getExecutor().execute(() -> {
                    try {
                        options.getWriter().delete(deletedKeys);
                    } catch (Exception ex) {
                        future.completeExceptionally(ex);
                        return;
                    }
                    future.complete((long) deletedKeys.size());
                });
                return future;
            }
        });
        return new CompletableFutureWrapper<>(f);
    }

    protected RFuture> fastRemoveOperationBatchAsync(K... keys) {
        List args = new ArrayList<>(keys.length);
        for (K key : keys) {
            args.add(encodeMapKey(key));
        }

        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());
        for (K key : keys) {
            args.add(encodeMapKey(key));
        }
        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, 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(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 RedissonMapIterator(RedissonMap.this, pattern, count) {
            @Override
            protected K getValue(java.util.Map.Entry entry) {
                return (K) entry.getKey();
            }
        };
    }
    
    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) {
        CompletableFuture result = new CompletableFuture<>();
        commandExecutor.getConnectionManager().getExecutor().execute(new Runnable() {
            @Override
            public void run() {
                V value;
                try {
                    value = options.getLoader().load(key);
                    if (value == null) {
                        lock.unlockAsync(threadId)
                                .whenComplete((r, e) -> {
                                    if (e != null) {
                                        result.completeExceptionally(e);
                                        return;
                                    }

                                    result.complete(value);
                                });
                        return;
                    }
                } catch (Exception e) {
                    log.error("Unable to load value by key " + key + " for map " + getRawName(), e);
                    lock.unlockAsync(threadId)
                            .whenComplete((r, ex) -> {
                                if (ex != null) {
                                    result.completeExceptionally(ex);
                                    return;
                                }

                                result.complete(null);
                            });
                    return;
                }
                    
                putOperationAsync(key, value)
                        .whenComplete((res, e) -> {
                    if (e != null) {
                        lock.unlockAsync(threadId);
                        result.completeExceptionally(e);
                        return;
                    }

                    lock.unlockAsync(threadId)
                            .whenComplete((r, ex) -> {
                                if (ex != null) {
                                    result.completeExceptionally(ex);
                                    return;
                                }

                                result.complete(value);
                            });
                });
            }
        });
        return result;
    }

    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();
        }

    }

}