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.RedissonSetCache Maven / Gradle / Ivy
/**
* Copyright (c) 2013-2024 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;
import org.redisson.api.*;
import org.redisson.api.listener.SetAddListener;
import org.redisson.api.listener.SetRemoveListener;
import org.redisson.api.listener.TrackingListener;
import org.redisson.api.mapreduce.RCollectionMapReduce;
import org.redisson.client.RedisClient;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.IntegerCodec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.eviction.EvictionScheduler;
import org.redisson.iterator.RedissonBaseIterator;
import org.redisson.mapreduce.RedissonCollectionMapReduce;
import org.redisson.misc.CompletableFutureWrapper;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
/**
* Set-based cache with ability to set TTL for each entry via
* {@link RSetCache#add(Object, long, TimeUnit)} method.
*
*
* Current Redis implementation doesn't have set entry eviction functionality.
* Thus values are checked for TTL expiration during any value read operation.
* If entry expired then it doesn't returns and clean task runs asynchronous.
* Clean task deletes removes 100 expired entries at once.
* In addition there is {@link org.redisson.eviction.EvictionScheduler}. This scheduler
* deletes expired entries in time interval between 5 seconds to 2 hours.
*
* If eviction is not required then it's better to use {@link org.redisson.api.RSet}.
*
* @author Nikita Koksharov
*
* @param value
*/
public class RedissonSetCache extends RedissonExpirable implements RSetCache, ScanIterator {
final RedissonClient redisson;
final EvictionScheduler evictionScheduler;
public RedissonSetCache(EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name);
if (evictionScheduler != null) {
evictionScheduler.schedule(getRawName(), 0);
}
this.evictionScheduler = evictionScheduler;
this.redisson = redisson;
}
public RedissonSetCache(Codec codec, EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name);
if (evictionScheduler != null) {
evictionScheduler.schedule(getRawName(), 0);
}
this.evictionScheduler = evictionScheduler;
this.redisson = redisson;
}
@Override
public RCollectionMapReduce mapReduce() {
return new RedissonCollectionMapReduce<>(this, redisson, commandExecutor);
}
@Override
public int size() {
return get(sizeAsync());
}
@Override
public RFuture sizeAsync() {
return commandExecutor.evalReadAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_INTEGER,
"local values = redis.call('zrangebyscore', KEYS[1], ARGV[1], ARGV[2]);" +
"return #values;",
Arrays.asList(getRawName()),
System.currentTimeMillis(), 92233720368547758L);
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public boolean contains(Object o) {
return get(containsAsync(o));
}
@Override
public RFuture containsAsync(Object o) {
String name = getRawName(o);
return commandExecutor.evalReadAsync(name, codec, RedisCommands.EVAL_BOOLEAN,
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); " +
"if expireDateScore ~= false then " +
"if tonumber(expireDateScore) <= tonumber(ARGV[1]) then " +
"return 0;" +
"end;" +
"return 1;" +
"end; " +
"return 0;",
Arrays.asList(name),
System.currentTimeMillis(), encode(o));
}
@Override
public ScanResult scanIterator(String name, RedisClient client, String startPos, String pattern, int count) {
RFuture> f = scanIteratorAsync(name, client, startPos, pattern, count);
return get(f);
}
@Override
public RFuture> scanIteratorAsync(String name, RedisClient client, String startPos, String pattern, int count) {
List params = new ArrayList<>();
params.add(startPos);
params.add(System.currentTimeMillis());
if (pattern != null) {
params.add(pattern);
}
params.add(count);
return commandExecutor.evalReadAsync(client, name, codec, RedisCommands.EVAL_SCAN,
"local result = {}; "
+ "local res; "
+ "if (#ARGV == 4) then "
+ " res = redis.call('zscan', KEYS[1], ARGV[1], 'match', ARGV[3], 'count', ARGV[4]); "
+ "else "
+ " res = redis.call('zscan', KEYS[1], ARGV[1], 'count', ARGV[3]); "
+ "end;"
+ "for i, value in ipairs(res[2]) do "
+ "if i % 2 == 0 then "
+ "local expireDate = value; "
+ "if tonumber(expireDate) > tonumber(ARGV[2]) then "
+ "table.insert(result, res[2][i-1]); "
+ "end; "
+ "end;"
+ "end;"
+ "return {res[1], result};", Arrays.asList(name), params.toArray());
}
@Override
public Iterator iterator(int count) {
return iterator(null, count);
}
@Override
public Iterator iterator(String pattern) {
return iterator(pattern, 10);
}
@Override
public Iterator iterator(String pattern, int count) {
return new RedissonBaseIterator() {
@Override
protected ScanResult iterator(RedisClient client, String nextIterPos) {
return scanIterator(getRawName(), client, nextIterPos, pattern, count);
}
@Override
protected void remove(Object value) {
RedissonSetCache.this.remove((V) value);
}
};
}
@Override
public Iterator iterator() {
return iterator(null);
}
@Override
public Set readAll() {
return get(readAllAsync());
}
@Override
public RFuture> readAllAsync() {
return commandExecutor.readAsync(getRawName(), codec, RedisCommands.ZRANGEBYSCORE, getRawName(), System.currentTimeMillis(), 92233720368547758L);
}
@Override
public Object[] toArray() {
Set res = get(readAllAsync());
return res.toArray();
}
@Override
public T[] toArray(T[] a) {
Set res = get(readAllAsync());
return res.toArray(a);
}
@Override
public boolean add(V e) {
return get(addAsync(e));
}
@Override
public boolean add(V value, long ttl, TimeUnit unit) {
return get(addAsync(value, ttl, unit));
}
@Override
public RFuture addAsync(V value, long ttl, TimeUnit unit) {
if (ttl < 0) {
throw new IllegalArgumentException("TTL can't be negative");
}
if (ttl == 0) {
return addAsync(value);
}
if (unit == null) {
throw new NullPointerException("TimeUnit param can't be null");
}
ByteBuf objectState = encode(value);
long timeoutDate = System.currentTimeMillis() + unit.toMillis(ttl);
String name = getRawName(value);
return commandExecutor.evalWriteAsync(name, codec, RedisCommands.EVAL_BOOLEAN,
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[3]); " +
"redis.call('zadd', KEYS[1], ARGV[2], ARGV[3]); " +
"if expireDateScore ~= false and tonumber(expireDateScore) > tonumber(ARGV[1]) then " +
"return 0;" +
"end; " +
"return 1; ",
Arrays.asList(name), System.currentTimeMillis(), timeoutDate, objectState);
}
@Override
public boolean tryAdd(V... values) {
return get(tryAddAsync(values));
}
@Override
public RFuture tryAddAsync(V... values) {
return tryAddAsync(92233720368547758L - System.currentTimeMillis(), TimeUnit.MILLISECONDS, values);
}
@Override
public boolean tryAdd(long ttl, TimeUnit unit, V... values) {
return get(tryAddAsync(ttl, unit, values));
}
@Override
public RFuture tryAddAsync(long ttl, TimeUnit unit, V... values) {
long timeoutDate = System.currentTimeMillis() + unit.toMillis(ttl);
if (ttl == 0) {
timeoutDate = 92233720368547758L - System.currentTimeMillis();
}
List params = new ArrayList<>();
params.add(System.currentTimeMillis());
params.add(timeoutDate);
params.addAll(encode(Arrays.asList(values)));
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN,
"for i, v in ipairs(ARGV) do " +
"local expireDateScore = redis.call('zscore', KEYS[1], v); " +
"if expireDateScore ~= false and tonumber(expireDateScore) > tonumber(ARGV[1]) then " +
"return 0; " +
"end; " +
"end; " +
"for i=3, #ARGV, 1 do " +
"redis.call('zadd', KEYS[1], ARGV[2], ARGV[i]); " +
"end; " +
"return 1; ",
Arrays.asList(getRawName()), params.toArray());
}
@Override
public RFuture addAsync(V value) {
return addAsync(value, 92233720368547758L - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public RFuture removeAsync(Object o) {
String name = getRawName(o);
return commandExecutor.writeAsync(name, codec, RedisCommands.ZREM, name, encode(o));
}
@Override
public boolean remove(Object value) {
return get(removeAsync((V) value));
}
@Override
public boolean containsAll(Collection> c) {
return get(containsAllAsync(c));
}
@Override
public RFuture containsAllAsync(Collection> c) {
if (c.isEmpty()) {
return new CompletableFutureWrapper<>(true);
}
List params = new ArrayList(c.size() + 1);
params.add(System.currentTimeMillis());
encode(params, c);
return commandExecutor.evalReadAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN,
"for j = 2, #ARGV, 1 do "
+ "local expireDateScore = redis.call('zscore', KEYS[1], ARGV[j]) "
+ "if expireDateScore ~= false then "
+ "if tonumber(expireDateScore) <= tonumber(ARGV[1]) then "
+ "return 0;"
+ "end; "
+ "else "
+ "return 0;"
+ "end; "
+ "end; "
+ "return 1; ",
Collections.singletonList(getRawName()), params.toArray());
}
@Override
public boolean addAll(Collection extends V> c) {
return get(addAllAsync(c));
}
@Override
public RFuture addAllAsync(Collection extends V> c) {
if (c.isEmpty()) {
return new CompletableFutureWrapper<>(false);
}
long score = 92233720368547758L - System.currentTimeMillis();
List params = new ArrayList(c.size()*2 + 1);
params.add(getRawName());
for (V value : c) {
ByteBuf objectState = encode(value);
params.add(score);
params.add(objectState);
}
return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.ZADD_BOOL_RAW, params.toArray());
}
@Override
public boolean retainAll(Collection> c) {
return get(retainAllAsync(c));
}
@Override
public RFuture retainAllAsync(Collection> c) {
if (c.isEmpty()) {
return deleteAsync();
}
long score = 92233720368547758L - System.currentTimeMillis();
List params = new ArrayList<>(c.size() * 2);
for (Object object : c) {
params.add(score);
encode(params, object);
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN,
"redis.call('zadd', KEYS[2], unpack(ARGV)); "
+ "local prevSize = redis.call('zcard', KEYS[1]); "
+ "local size = redis.call('zinterstore', KEYS[1], #ARGV/2, KEYS[1], KEYS[2], 'aggregate', 'min');"
+ "redis.call('del', KEYS[2]); "
+ "return size ~= prevSize and 1 or 0; ",
Arrays.asList(getRawName(), "redisson_temp__{" + getRawName() + "}"), params.toArray());
}
@Override
public RFuture removeAllAsync(Collection> c) {
if (c.isEmpty()) {
return new CompletableFutureWrapper<>(false);
}
List params = new ArrayList(c.size()+1);
params.add(getRawName());
encode(params, c);
return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.ZREM, params.toArray());
}
@Override
public boolean removeAll(Collection> c) {
return get(removeAllAsync(c));
}
@Override
public void clear() {
delete();
}
@Override
public RPermitExpirableSemaphore getPermitExpirableSemaphore(V value) {
String lockName = getLockByValue(value, "permitexpirablesemaphore");
return new RedissonPermitExpirableSemaphore(commandExecutor, lockName);
}
@Override
public RSemaphore getSemaphore(V value) {
String lockName = getLockByValue(value, "semaphore");
return new RedissonSemaphore(commandExecutor, lockName);
}
@Override
public RCountDownLatch getCountDownLatch(V value) {
String lockName = getLockByValue(value, "countdownlatch");
return new RedissonCountDownLatch(commandExecutor, lockName);
}
@Override
public RLock getFairLock(V value) {
String lockName = getLockByValue(value, "fairlock");
return new RedissonFairLock(commandExecutor, lockName);
}
@Override
public RLock getLock(V value) {
String lockName = getLockByValue(value, "lock");
return new RedissonLock(commandExecutor, lockName);
}
@Override
public RReadWriteLock getReadWriteLock(V value) {
String lockName = getLockByValue(value, "rw_lock");
return new RedissonReadWriteLock(commandExecutor, lockName);
}
@Override
public void destroy() {
if (evictionScheduler != null) {
evictionScheduler.remove(getRawName());
}
removeListeners();
}
@Override
public Stream stream(int count) {
return toStream(iterator(count));
}
@Override
public Stream stream(String pattern, int count) {
return toStream(iterator(pattern, count));
}
@Override
public Stream stream(String pattern) {
return toStream(iterator(pattern));
}
@Override
public int addAllCounted(Collection extends V> c) {
return get(addAllCountedAsync(c));
}
@Override
public int removeAllCounted(Collection extends V> c) {
return get(removeAllCountedAsync(c));
}
@Override
public Iterator distributedIterator(String pattern) {
String iteratorName = "__redisson_scored_sorted_set_cursor_{" + getRawName() + "}";
return distributedIterator(iteratorName, pattern, 10);
}
@Override
public Iterator distributedIterator(int count) {
String iteratorName = "__redisson_scored_sorted_set_cursor_{" + getRawName() + "}";
return distributedIterator(iteratorName, null, count);
}
@Override
public Iterator distributedIterator(String iteratorName, String pattern, int count) {
return new RedissonBaseIterator() {
@Override
protected ScanResult iterator(RedisClient client, String nextIterPos) {
return distributedScanIterator(iteratorName, pattern, count);
}
@Override
protected void remove(Object value) {
RedissonSetCache.this.remove(value);
}
};
}
private ScanResult distributedScanIterator(String iteratorName, String pattern, int count) {
return get(distributedScanIteratorAsync(iteratorName, pattern, count));
}
private RFuture> distributedScanIteratorAsync(String iteratorName, String pattern, int count) {
List args = new ArrayList<>(2);
args.add(System.currentTimeMillis());
if (pattern != null) {
args.add(pattern);
}
args.add(count);
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_SCAN,
"local cursor = redis.call('get', KEYS[2]); "
+ "if cursor ~= false then "
+ "cursor = tonumber(cursor); "
+ "else "
+ "cursor = 0;"
+ "end;"
+ "if cursor == -1 then "
+ "return {0, {}}; "
+ "end;"
+ "local result; "
+ "if (#ARGV == 3) then "
+ "result = redis.call('zscan', KEYS[1], cursor, 'match', ARGV[2], 'count', ARGV[3]); "
+ "else "
+ "result = redis.call('zscan', KEYS[1], cursor, 'count', ARGV[2]); "
+ "end;"
+ "local next_cursor = result[1]"
+ "if next_cursor ~= \"0\" then "
+ "redis.call('setex', KEYS[2], 3600, next_cursor);"
+ "else "
+ "redis.call('setex', KEYS[2], 3600, -1);"
+ "end; "
+ "local res = {};"
+ "for i, value in ipairs(result[2]) do "
+ "if i % 2 == 0 then "
+ "local expireDate = value; "
+ "if tonumber(expireDate) > tonumber(ARGV[1]) then "
+ "table.insert(res, result[2][i-1]); "
+ "end; "
+ "end; "
+ "end;"
+ "return {result[1], res};",
Arrays.asList(getRawName(), iteratorName), args.toArray());
}
@Override
public Set removeRandom(int amount) {
throw new UnsupportedOperationException();
}
@Override
public V removeRandom() {
throw new UnsupportedOperationException();
}
@Override
public V random() {
return get(randomAsync());
}
@Override
public Set random(int count) {
return get(randomAsync(count));
}
@Override
public boolean move(String destination, V member) {
throw new UnsupportedOperationException();
}
@Override
public int union(String... names) {
return get(unionAsync(names));
}
@Override
public Set readUnion(String... names) {
return get(readUnionAsync(names));
}
@Override
public int diff(String... names) {
return get(diffAsync(names));
}
@Override
public Set readDiff(String... names) {
return get(readDiffAsync(names));
}
@Override
public int intersection(String... names) {
return get(intersectionAsync(names));
}
@Override
public Set readIntersection(String... names) {
return get(readIntersectionAsync(names));
}
@Override
public Integer countIntersection(String... names) {
return get(countIntersectionAsync(names));
}
@Override
public Integer countIntersection(int limit, String... names) {
return get(countIntersectionAsync(limit, names));
}
@Override
public List containsEach(Collection c) {
throw new UnsupportedOperationException();
}
@Override
public RFuture> removeRandomAsync(int amount) {
throw new UnsupportedOperationException();
}
@Override
public RFuture removeRandomAsync() {
throw new UnsupportedOperationException();
}
@Override
public RFuture randomAsync() {
String tempName = prefixName("__redisson_cache_temp", getRawName());
return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_OBJECT,
"local values = redis.call('zrangebyscore', KEYS[1], ARGV[1], ARGV[2], 'WITHSCORES');" +
"for i = 1, #values, 2 do "
+ "redis.call('zadd', KEYS[2], values[i], values[i+1]); " +
"end;" +
"local res = redis.call('zrandmember', KEYS[2]); " +
"redis.call('del', KEYS[2]); " +
"return res;",
Arrays.asList(getRawName(), tempName),
System.currentTimeMillis(), 92233720368547758L);
}
@Override
public RFuture> randomAsync(int count) {
String tempName = prefixName("__redisson_cache_temp", getRawName());
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_SET,
"local values = redis.call('zrangebyscore', KEYS[1], ARGV[1], ARGV[2], 'WITHSCORES');" +
"for i = 1, #values, 2 do "
+ "redis.call('zadd', KEYS[2], values[i], values[i+1]); " +
"end;" +
"local res = redis.call('zrandmember', KEYS[2], ARGV[3]); " +
"redis.call('del', KEYS[2]); " +
"return res;",
Arrays.asList(getRawName(), tempName),
System.currentTimeMillis(), 92233720368547758L, count);
}
@Override
public RFuture moveAsync(String destination, V member) {
throw new UnsupportedOperationException();
}
@Override
public RFuture unionAsync(String... names) {
List keys = new LinkedList<>();
keys.add(getRawName());
keys.addAll(Arrays.asList(names));
for (Object key : names) {
String tempName = prefixName("__redisson_cache_temp", key.toString());
keys.add(tempName);
}
return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_INTEGER,
"local args = {KEYS[1], (#KEYS-1)/2};" +
"for i = 2, (#KEYS-1)/2 + 1, 1 do " +
"local values = redis.call('zrangebyscore', KEYS[i], ARGV[1], ARGV[2], 'WITHSCORES');" +
"local k = (#KEYS-1)/2 + i; " +
"table.insert(args, KEYS[k]); " +
"for j = 1, #values, 2 do " +
"redis.call('zadd', KEYS[k], values[j+1], values[j]); " +
"end;" +
"end; " +
"table.insert(args, 'AGGREGATE'); " +
"table.insert(args, 'SUM'); " +
"local res = redis.call('zunionstore', unpack(args));" +
"redis.call('del', unpack(KEYS, (#KEYS-1)/2+2, #KEYS)); " +
"return res;",
keys,
System.currentTimeMillis(), 92233720368547758L, names.length+1);
}
@Override
public RFuture> readUnionAsync(String... names) {
List keys = new LinkedList<>();
keys.add(getRawName());
keys.addAll(Arrays.asList(names));
for (Object key : new ArrayList<>(keys)) {
String tempName = prefixName("__redisson_cache_temp", key.toString());
keys.add(tempName);
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_SET,
"for i = 1, #KEYS, 1 do " +
"local values = redis.call('zrangebyscore', KEYS[ARGV[3] + i], ARGV[1], ARGV[2], 'WITHSCORES');" +
"for j = 1, #values, 2 do "
+ "redis.call('zadd', KEYS[ARGV[3] + i], values[j], values[j+1]); " +
"end;" +
"end; " +
"local values = redis.call('zunion', ARGV[3], unpack(KEYS, ARGV[3], #ARGV), 'AGGREGATE', 'SUM');" +
"redis.call('del', unpack(KEYS, ARGV[3], #KEYS)); " +
"return values;",
keys,
System.currentTimeMillis(), 92233720368547758L, names.length+1);
}
@Override
public RFuture diffAsync(String... names) {
List keys = new LinkedList<>();
keys.add(getRawName());
keys.addAll(Arrays.asList(names));
for (Object key : names) {
String tempName = prefixName("__redisson_cache_temp", key.toString());
keys.add(tempName);
}
return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_INTEGER,
"local args = {KEYS[1], (#KEYS-1)/2};" +
"for i = 2, (#KEYS-1)/2 + 1, 1 do " +
"local values = redis.call('zrangebyscore', KEYS[i], ARGV[1], ARGV[2], 'WITHSCORES');" +
"local k = (#KEYS-1)/2 + i; " +
"table.insert(args, KEYS[k]); " +
"for j = 1, #values, 2 do " +
"redis.call('zadd', KEYS[k], values[j+1], values[j]); " +
"end;" +
"end; " +
"local res = redis.call('zdiffstore', unpack(args));" +
"redis.call('del', unpack(KEYS, (#KEYS-1)/2+2, #KEYS)); " +
"return res;",
keys,
System.currentTimeMillis(), 92233720368547758L, names.length+1);
}
@Override
public RFuture> readDiffAsync(String... names) {
List keys = new LinkedList<>();
keys.add(getRawName());
keys.addAll(Arrays.asList(names));
for (Object key : new ArrayList<>(keys)) {
String tempName = prefixName("__redisson_cache_temp", key.toString());
keys.add(tempName);
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_SET,
"for i = 1, #KEYS, 1 do " +
"local values = redis.call('zrangebyscore', KEYS[ARGV[3] + i], ARGV[1], ARGV[2], 'WITHSCORES');" +
"for j = 1, #values, 2 do "
+ "redis.call('zadd', KEYS[ARGV[3] + i], values[j], values[j+1]); " +
"end;" +
"end; " +
"local values = redis.call('zdiff', ARGV[3], unpack(KEYS, ARGV[3], #ARGV));" +
"redis.call('del', unpack(KEYS, ARGV[3], #KEYS)); " +
"return values;",
keys,
System.currentTimeMillis(), 92233720368547758L, names.length+1);
}
@Override
public RFuture intersectionAsync(String... names) {
List keys = new LinkedList<>();
keys.add(getRawName());
keys.addAll(Arrays.asList(names));
for (Object key : names) {
String tempName = prefixName("__redisson_cache_temp", key.toString());
keys.add(tempName);
}
return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_INTEGER,
"local args = {KEYS[1], (#KEYS-1)/2};" +
"for i = 2, (#KEYS-1)/2 + 1, 1 do " +
"local values = redis.call('zrangebyscore', KEYS[i], ARGV[1], ARGV[2], 'WITHSCORES');" +
"local k = (#KEYS-1)/2 + i; " +
"table.insert(args, KEYS[k]); " +
"for j = 1, #values, 2 do " +
"redis.call('zadd', KEYS[k], values[j+1], values[j]); " +
"end;" +
"end; " +
"table.insert(args, 'AGGREGATE'); " +
"table.insert(args, 'SUM'); " +
"local res = redis.call('zinterstore', unpack(args));" +
"redis.call('del', unpack(KEYS, (#KEYS-1)/2+2, #KEYS)); " +
"return res;",
keys,
System.currentTimeMillis(), 92233720368547758L, names.length+1);
}
@Override
public RFuture> readIntersectionAsync(String... names) {
List keys = new LinkedList<>();
keys.add(getRawName());
keys.addAll(Arrays.asList(names));
for (Object key : new ArrayList<>(keys)) {
String tempName = prefixName("__redisson_cache_temp", key.toString());
keys.add(tempName);
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_SET,
"for i = 1, #KEYS, 1 do " +
"local values = redis.call('zrangebyscore', KEYS[ARGV[3] + i], ARGV[1], ARGV[2], 'WITHSCORES');" +
"for j = 1, #values, 2 do "
+ "redis.call('zadd', KEYS[ARGV[3] + i], values[j], values[j+1]); " +
"end;" +
"end; " +
"local values = redis.call('zinter', ARGV[3], unpack(KEYS, ARGV[3], #ARGV), 'AGGREGATE', 'SUM');" +
"redis.call('del', unpack(KEYS, ARGV[3], #KEYS)); " +
"return values;",
keys,
System.currentTimeMillis(), 92233720368547758L, names.length+1);
}
@Override
public RFuture countIntersectionAsync(String... names) {
return countIntersectionAsync(0, names);
}
@Override
public RFuture countIntersectionAsync(int limit, String... names) {
List keys = new LinkedList<>();
keys.add(getRawName());
keys.addAll(Arrays.asList(names));
for (Object key : new ArrayList<>(keys)) {
String tempName = prefixName("__redisson_cache_temp", key.toString());
keys.add(tempName);
}
return commandExecutor.evalWriteAsync(getRawName(), IntegerCodec.INSTANCE, RedisCommands.EVAL_INTEGER,
"local args = {ARGV[3]};" +
"for i = 1, ARGV[3], 1 do " +
"local values = redis.call('zrangebyscore', KEYS[i], ARGV[1], ARGV[2], 'WITHSCORES');" +
"local k = tonumber(ARGV[3]) + i; " +
"table.insert(args, KEYS[k]); " +
"for j = 1, #values, 2 do " +
"redis.call('zadd', KEYS[k], values[j+1], values[j]); " +
"end;" +
"end; " +
"table.insert(args, 'LIMIT'); " +
"table.insert(args, ARGV[4]); " +
"local res = redis.call('zintercard', unpack(args));" +
"redis.call('del', unpack(KEYS, ARGV[3]+1, #KEYS)); " +
"return res;",
keys,
System.currentTimeMillis(), 92233720368547758L, names.length+1, limit);
}
@Override
public RFuture addAllCountedAsync(Collection extends V> c) {
if (c.isEmpty()) {
return new CompletableFutureWrapper<>(0);
}
List args = new ArrayList<>(c.size() + 1);
args.add(getRawName());
for (V v : c) {
args.add(92233720368547758L);
try {
args.add(v);
} catch (Exception e) {
args.forEach(vv -> {
ReferenceCountUtil.safeRelease(vv);
});
throw e;
}
}
return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.ZADD_INT, args.toArray());
}
@Override
public RFuture removeAllCountedAsync(Collection extends V> c) {
if (c.isEmpty()) {
return new CompletableFutureWrapper<>(0);
}
List args = new ArrayList<>(c.size() + 1);
args.add(getRawName());
encode(args, c);
return commandExecutor.writeAsync(getRawName(), codec, RedisCommands.ZREM_INT, args.toArray());
}
@Override
public RFuture> containsEachAsync(Collection c) {
throw new UnsupportedOperationException();
}
@Override
public Set readSort(SortOrder order) {
return get(readSortAsync(order));
}
@Override
public Set readSort(SortOrder order, int offset, int count) {
return get(readSortAsync(order, offset, count));
}
@Override
public Set readSort(String byPattern, SortOrder order) {
return get(readSortAsync(byPattern, order));
}
@Override
public Set readSort(String byPattern, SortOrder order, int offset, int count) {
return get(readSortAsync(byPattern, order, offset, count));
}
@Override
public Collection readSort(String byPattern, List getPatterns, SortOrder order) {
return get(readSortAsync(byPattern, getPatterns, order));
}
@Override
public Collection readSort(String byPattern, List getPatterns, SortOrder order, int offset, int count) {
return get(readSortAsync(byPattern, getPatterns, order, offset, count));
}
@Override
public Set readSortAlpha(SortOrder order) {
return get(readSortAlphaAsync(order));
}
@Override
public Set readSortAlpha(SortOrder order, int offset, int count) {
return get(readSortAlphaAsync(order, offset, count));
}
@Override
public Set readSortAlpha(String byPattern, SortOrder order) {
return get(readSortAlphaAsync(byPattern, order));
}
@Override
public Set readSortAlpha(String byPattern, SortOrder order, int offset, int count) {
return get(readSortAlphaAsync(byPattern, order, offset, count));
}
@Override
public Collection readSortAlpha(String byPattern, List getPatterns, SortOrder order) {
return get(readSortAlphaAsync(byPattern, getPatterns, order));
}
@Override
public Collection readSortAlpha(String byPattern, List getPatterns, SortOrder order, int offset, int count) {
return get(readSortAlphaAsync(byPattern, getPatterns, order, offset, count));
}
@Override
public int sortTo(String destName, SortOrder order) {
return get(sortToAsync(destName, order));
}
@Override
public int sortTo(String destName, SortOrder order, int offset, int count) {
return get(sortToAsync(destName, order, offset, count));
}
@Override
public int sortTo(String destName, String byPattern, SortOrder order) {
return get(sortToAsync(destName, byPattern, order));
}
@Override
public int sortTo(String destName, String byPattern, SortOrder order, int offset, int count) {
return get(sortToAsync(destName, byPattern, order, offset, count));
}
@Override
public int sortTo(String destName, String byPattern, List getPatterns, SortOrder order) {
return get(sortToAsync(destName, byPattern, getPatterns, order));
}
@Override
public int sortTo(String destName, String byPattern, List getPatterns, SortOrder order, int offset, int count) {
return get(sortToAsync(destName, byPattern, getPatterns, order, offset, count));
}
@Override
public RFuture> readSortAsync(SortOrder order) {
return readSortAsync(null, null, order, -1, -1, false);
}
@Override
public RFuture> readSortAsync(SortOrder order, int offset, int count) {
return readSortAsync(null, null, order, offset, count, false);
}
@Override
public RFuture> readSortAsync(String byPattern, SortOrder order) {
return readSortAsync(byPattern, null, order, -1, -1, false);
}
@Override
public RFuture> readSortAsync(String byPattern, SortOrder order, int offset, int count) {
return readSortAsync(byPattern, null, order, offset, count, false);
}
@Override
public RFuture> readSortAsync(String byPattern, List getPatterns, SortOrder order) {
return readSortAsync(byPattern, getPatterns, order, -1, -1);
}
@Override
public RFuture> readSortAsync(String byPattern, List getPatterns, SortOrder order, int offset, int count) {
return readSortAsync(byPattern, getPatterns, order, offset, count, false);
}
private RFuture readSortAsync(String byPattern, List getPatterns, SortOrder order, int offset, int count, boolean alpha) {
throw new UnsupportedOperationException();
// List params = new ArrayList<>();
// params.add(System.currentTimeMillis());
// params.add(92233720368547758L);
// if (byPattern != null) {
// params.add("BY");
// params.add(byPattern);
// }
// if (offset != -1 && count != -1) {
// params.add("LIMIT");
// }
// if (offset != -1) {
// params.add(offset);
// }
// if (count != -1) {
// params.add(count);
// }
// if (getPatterns != null) {
// for (String pattern : getPatterns) {
// params.add("GET");
// params.add(pattern);
// }
// }
// if (alpha) {
// params.add("ALPHA");
// }
// if (order != null) {
// params.add(order);
// }
//
// String tempName = prefixName("__redisson_cache_temp", getRawName());
// return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_SET,
// "local values = redis.call('zrangebyscore', KEYS[1], ARGV[1], ARGV[2], 'WITHSCORES');" +
// "for i = 1, #values, 2 do "
// + "redis.call('zadd', KEYS[2], values[i], values[i+1]); " +
// "end;" +
// "local res = redis.call('sort', KEYS[2], unpack(ARGV, 3, #ARGV)); " +
// "redis.call('del', KEYS[2]); " +
// "return res;",
// Arrays.asList(getRawName(), tempName), params.toArray());
}
@Override
public RFuture> readSortAlphaAsync(SortOrder order) {
return readSortAsync(null, null, order, -1, -1, true);
}
@Override
public RFuture> readSortAlphaAsync(SortOrder order, int offset, int count) {
return readSortAsync(null, null, order, offset, count, true);
}
@Override
public RFuture> readSortAlphaAsync(String byPattern, SortOrder order) {
return readSortAsync(byPattern, null, order, -1, -1, true);
}
@Override
public RFuture> readSortAlphaAsync(String byPattern, SortOrder order, int offset, int count) {
return readSortAsync(byPattern, null, order, offset, count, true);
}
@Override
public RFuture> readSortAlphaAsync(String byPattern, List getPatterns, SortOrder order) {
return readSortAsync(byPattern, getPatterns, order, -1, -1, true);
}
@Override
public RFuture> readSortAlphaAsync(String byPattern, List getPatterns, SortOrder order, int offset, int count) {
return readSortAsync(byPattern, getPatterns, order, offset, count, true);
}
@Override
public RFuture sortToAsync(String destName, SortOrder order) {
return sortToAsync(destName, null, null, order, -1, -1);
}
@Override
public RFuture sortToAsync(String destName, SortOrder order, int offset, int count) {
return sortToAsync(destName, null, null, order, offset, count);
}
@Override
public RFuture sortToAsync(String destName, String byPattern, SortOrder order) {
return sortToAsync(destName, byPattern, null, order, -1, -1);
}
@Override
public RFuture sortToAsync(String destName, String byPattern, SortOrder order, int offset, int count) {
return sortToAsync(destName, byPattern, null, order, offset, count);
}
@Override
public RFuture sortToAsync(String destName, String byPattern, List getPatterns, SortOrder order) {
return sortToAsync(destName, byPattern, getPatterns, order, -1, -1);
}
@Override
public RFuture sortToAsync(String destName, String byPattern, List getPatterns, SortOrder order, int offset, int count) {
throw new UnsupportedOperationException();
// List params = new ArrayList<>();
// params.add(System.currentTimeMillis());
// params.add(92233720368547758L);
// String tempName = prefixName("__redisson_cache_temp", getRawName());
// params.add(tempName);
// if (byPattern != null) {
// params.add("BY");
// params.add(byPattern);
// }
// if (offset != -1 && count != -1) {
// params.add("LIMIT");
// }
// if (offset != -1) {
// params.add(offset);
// }
// if (count != -1) {
// params.add(count);
// }
// if (getPatterns != null) {
// for (String pattern : getPatterns) {
// params.add("GET");
// params.add(pattern);
// }
// }
// params.add(order);
// params.add("STORE");
// params.add(destName);
//
// return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_INTEGER,
// "local values = redis.call('zrangebyscore', KEYS[1], ARGV[1], ARGV[2], 'WITHSCORES');" +
// "for i = 1, #values, 2 do "
// + "redis.call('zadd', KEYS[2], values[i], values[i+1]); " +
// "end;" +
// "local res = redis.call('sort', unpack(ARGV, 3, #ARGV)); " +
// "redis.call('del', KEYS[2]); " +
// "return res;",
// Arrays.asList(getRawName(), tempName), params.toArray());
}
@Override
public boolean addIfAbsent(Duration ttl, V object) {
return get(addIfAbsentAsync(ttl, object));
}
@Override
public boolean addIfExists(Duration ttl, V object) {
return get(addIfExistsAsync(ttl, object));
}
@Override
public boolean addIfLess(Duration ttl, V object) {
return get(addIfLessAsync(ttl, object));
}
@Override
public boolean addIfGreater(Duration ttl, V object) {
return get(addIfGreaterAsync(ttl, object));
}
@Override
public RFuture addIfAbsentAsync(Duration ttl, V object) {
long timeoutDate = System.currentTimeMillis() + ttl.toMillis();
if (ttl.isZero()) {
timeoutDate = 92233720368547758L - System.currentTimeMillis();
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN,
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[3]); " +
"if expireDateScore ~= false and tonumber(expireDateScore) > tonumber(ARGV[1]) then " +
"return 0; " +
"end; " +
"redis.call('zadd', KEYS[1], ARGV[2], ARGV[3]); " +
"return 1; ",
Arrays.asList(getRawName()),
System.currentTimeMillis(), timeoutDate, encode(object));
}
@Override
public RFuture addIfExistsAsync(Duration ttl, V object) {
long timeoutDate = System.currentTimeMillis() + ttl.toMillis();
if (ttl.isZero()) {
timeoutDate = 92233720368547758L - System.currentTimeMillis();
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN,
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[3]); " +
"if expireDateScore ~= false then " +
"if tonumber(expireDateScore) < tonumber(ARGV[1]) then " +
"return 0; " +
"end; " +
"redis.call('zadd', KEYS[1], ARGV[2], ARGV[3]); " +
"return 1; " +
"end; " +
"return 0; ",
Arrays.asList(getRawName()),
System.currentTimeMillis(), timeoutDate, encode(object));
}
@Override
public RFuture addIfLessAsync(Duration ttl, V object) {
long timeoutDate = System.currentTimeMillis() + ttl.toMillis();
if (ttl.isZero()) {
timeoutDate = 92233720368547758L - System.currentTimeMillis();
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN,
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[3]); " +
"if expireDateScore ~= false then " +
"if tonumber(expireDateScore) < tonumber(ARGV[1]) or tonumber(ARGV[2]) >= tonumber(expireDateScore) then " +
"return 0; " +
"end; " +
"redis.call('zadd', KEYS[1], ARGV[2], ARGV[3]); " +
"return 1; " +
"end; " +
"return 0; ",
Arrays.asList(getRawName()),
System.currentTimeMillis(), timeoutDate, encode(object));
}
@Override
public RFuture addIfGreaterAsync(Duration ttl, V object) {
long timeoutDate = System.currentTimeMillis() + ttl.toMillis();
if (ttl.isZero()) {
timeoutDate = 92233720368547758L - System.currentTimeMillis();
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN,
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[3]); " +
"if expireDateScore ~= false then " +
"if tonumber(expireDateScore) < tonumber(ARGV[1]) or tonumber(ARGV[2]) <= tonumber(expireDateScore) then " +
"return 0; " +
"end; " +
"redis.call('zadd', KEYS[1], ARGV[2], ARGV[3]); " +
"return 1; " +
"end; " +
"return 0; ",
Arrays.asList(getRawName()),
System.currentTimeMillis(), timeoutDate, encode(object));
}
@Override
public int addAllIfAbsent(Map objects) {
return get(addAllIfAbsentAsync(objects));
}
@Override
public boolean addIfAbsent(Map objects) {
return get(addIfAbsentAsync(objects));
}
@Override
public int addAllIfExist(Map objects) {
return get(addAllIfExistAsync(objects));
}
@Override
public int addAllIfGreater(Map objects) {
return get(addAllIfGreaterAsync(objects));
}
@Override
public int addAllIfLess(Map objects) {
return get(addAllIfLessAsync(objects));
}
@Override
public RFuture addAllIfAbsentAsync(Map objects) {
List params = new ArrayList<>();
long currentTime = System.currentTimeMillis();
params.add(currentTime);
for (Map.Entry entry : objects.entrySet()) {
long timeoutDate = currentTime + entry.getValue().toMillis();
if (entry.getValue().isZero()) {
timeoutDate = 92233720368547758L - currentTime;
}
params.add(timeoutDate);
encode(params, entry.getKey());
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_INTEGER,
"local result = 0; " +
"for i=2, #ARGV, 2 do " +
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[i+1]); " +
"if expireDateScore == false or tonumber(expireDateScore) <= tonumber(ARGV[1]) then " +
"result = result + 1; " +
"redis.call('zadd', KEYS[1], ARGV[i], ARGV[i+1]); " +
"end; " +
"end; " +
"return result; ",
Arrays.asList(getRawName()), params.toArray());
}
@Override
public RFuture addIfAbsentAsync(Map objects) {
List params = new ArrayList<>();
long currentTime = System.currentTimeMillis();
params.add(currentTime);
for (Map.Entry entry : objects.entrySet()) {
long timeoutDate = currentTime + entry.getValue().toMillis();
if (entry.getValue().isZero()) {
timeoutDate = 92233720368547758L - currentTime;
}
params.add(timeoutDate);
encode(params, entry.getKey());
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_BOOLEAN,
"for i=2, #ARGV, 2 do " +
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[i+1]); " +
"if expireDateScore ~= false and tonumber(expireDateScore) > tonumber(ARGV[1]) then " +
"return 0; " +
"end; " +
"end; " +
"for i=2, #ARGV, 2 do " +
"redis.call('zadd', KEYS[1], ARGV[i], ARGV[i+1]); " +
"end; " +
"return 1; ",
Collections.singletonList(getRawName()), params.toArray());
}
@Override
public RFuture addAllIfExistAsync(Map objects) {
List params = new ArrayList<>();
long currentTime = System.currentTimeMillis();
params.add(currentTime);
for (Map.Entry entry : objects.entrySet()) {
long timeoutDate = currentTime + entry.getValue().toMillis();
if (entry.getValue().isZero()) {
timeoutDate = 92233720368547758L - currentTime;
}
params.add(timeoutDate);
encode(params, entry.getKey());
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_INTEGER,
"local result = 0; " +
"for i=2, #ARGV, 2 do " +
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[i+1]); " +
"if expireDateScore ~= false and tonumber(expireDateScore) > tonumber(ARGV[1]) then " +
"result = result + 1; " +
"redis.call('zadd', KEYS[1], ARGV[i], ARGV[i+1]); " +
"end; " +
"end; " +
"return result; ",
Arrays.asList(getRawName()), params.toArray());
}
@Override
public RFuture addAllIfGreaterAsync(Map objects) {
List params = new ArrayList<>();
long currentTime = System.currentTimeMillis();
params.add(currentTime);
for (Map.Entry entry : objects.entrySet()) {
long timeoutDate = currentTime + entry.getValue().toMillis();
if (entry.getValue().isZero()) {
timeoutDate = 92233720368547758L - currentTime;
}
params.add(timeoutDate);
encode(params, entry.getKey());
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_INTEGER,
"local result = 0; " +
"for i=2, #ARGV, 2 do " +
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[i+1]); " +
"if expireDateScore ~= false and tonumber(expireDateScore) > tonumber(ARGV[1]) and tonumber(ARGV[i]) > tonumber(expireDateScore) then " +
"result = result + 1; " +
"redis.call('zadd', KEYS[1], ARGV[i], ARGV[i+1]); " +
"end; " +
"end; " +
"return result; ",
Arrays.asList(getRawName()), params.toArray());
}
@Override
public RFuture addAllIfLessAsync(Map objects) {
List params = new ArrayList<>();
long currentTime = System.currentTimeMillis();
params.add(currentTime);
for (Map.Entry entry : objects.entrySet()) {
long timeoutDate = currentTime + entry.getValue().toMillis();
if (entry.getValue().isZero()) {
timeoutDate = 92233720368547758L - currentTime;
}
params.add(timeoutDate);
encode(params, entry.getKey());
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_INTEGER,
"local result = 0; " +
"for i=2, #ARGV, 2 do " +
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[i+1]); " +
"if expireDateScore ~= false and tonumber(expireDateScore) > tonumber(ARGV[1]) and tonumber(ARGV[i]) < tonumber(expireDateScore) then " +
"result = result + 1; " +
"redis.call('zadd', KEYS[1], ARGV[i], ARGV[i+1]); " +
"end; " +
"end; " +
"return result; ",
Arrays.asList(getRawName()), params.toArray());
}
@Override
public int addAll(Map objects) {
return get(addAllAsync(objects));
}
@Override
public RFuture addAllAsync(Map objects) {
List params = new ArrayList<>();
long currentTime = System.currentTimeMillis();
params.add(currentTime);
for (Map.Entry entry : objects.entrySet()) {
long timeoutDate = currentTime + entry.getValue().toMillis();
if (entry.getValue().isZero()) {
timeoutDate = 92233720368547758L - currentTime;
}
params.add(timeoutDate);
encode(params, entry.getKey());
}
return commandExecutor.evalWriteAsync(getRawName(), codec, RedisCommands.EVAL_INTEGER,
"local result = 0; " +
"for i=2, #ARGV, 2 do " +
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[i+1]); " +
"if not (expireDateScore ~= false and tonumber(expireDateScore) > tonumber(ARGV[1])) then " +
"result = result + 1; " +
"end; " +
"redis.call('zadd', KEYS[1], ARGV[i], ARGV[i+1]); " +
"end; " +
"return result; ",
Arrays.asList(getRawName()), params.toArray());
}
@Override
public int addListener(ObjectListener listener) {
if (listener instanceof SetAddListener) {
return addListener("__keyevent@*:zadd", (SetAddListener) listener, SetAddListener::onAdd);
}
if (listener instanceof SetRemoveListener) {
return addListener("__keyevent@*:zrem", (SetRemoveListener) listener, SetRemoveListener::onRemove);
}
if (listener instanceof TrackingListener) {
return addTrackingListener((TrackingListener) listener);
}
return super.addListener(listener);
}
@Override
public RFuture addListenerAsync(ObjectListener listener) {
if (listener instanceof SetAddListener) {
return addListenerAsync("__keyevent@*:zadd", (SetAddListener) listener, SetAddListener::onAdd);
}
if (listener instanceof SetRemoveListener) {
return addListenerAsync("__keyevent@*:zrem", (SetRemoveListener) listener, SetRemoveListener::onRemove);
}
if (listener instanceof TrackingListener) {
return addTrackingListenerAsync((TrackingListener) listener);
}
return super.addListenerAsync(listener);
}
@Override
public void removeListener(int listenerId) {
removeTrackingListener(listenerId);
removeListener(listenerId, "__keyevent@*:zadd", "__keyevent@*:zrem");
super.removeListener(listenerId);
}
@Override
public RFuture removeListenerAsync(int listenerId) {
RFuture f1 = removeTrackingListenerAsync(listenerId);
RFuture f2 = removeListenerAsync(listenerId,
"__keyevent@*:zadd", "__keyevent@*:zrem");
return new CompletableFutureWrapper<>(CompletableFuture.allOf(f1.toCompletableFuture(), f2.toCompletableFuture()));
}
}