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

org.redisson.RedissonSetCache Maven / Gradle / Ivy

Go to download

Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC

There is a newer version: 3.40.2
Show newest version
/**
 * 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 c) { return get(addAllAsync(c)); } @Override public RFuture addAllAsync(Collection 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 c) { return get(addAllCountedAsync(c)); } @Override public int removeAllCounted(Collection 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 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 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())); } }