diff --git a/redisson/src/main/java/org/redisson/RedissonFairLock.java b/redisson/src/main/java/org/redisson/RedissonFairLock.java index 9386e4f0a..158105546 100644 --- a/redisson/src/main/java/org/redisson/RedissonFairLock.java +++ b/redisson/src/main/java/org/redisson/RedissonFairLock.java @@ -218,7 +218,7 @@ public class RedissonFairLock extends RedissonLock implements RLock { // threadWaitTime "local lastThreadId = redis.call('lindex', KEYS[2], -1);" + "local ttl;" + - "if lastThreadId ~= false and lastThreadId ~= ARGV[2] then " + + "if lastThreadId ~= false and lastThreadId ~= ARGV[2] and redis.call('zscore', KEYS[3], lastThreadId) ~= false then " + "ttl = tonumber(redis.call('zscore', KEYS[3], lastThreadId)) - tonumber(ARGV[4]);" + "else " + "ttl = redis.call('pttl', KEYS[1]);" + diff --git a/redisson/src/main/java/org/redisson/RedissonObject.java b/redisson/src/main/java/org/redisson/RedissonObject.java index dc2fe2433..1cbfcb606 100644 --- a/redisson/src/main/java/org/redisson/RedissonObject.java +++ b/redisson/src/main/java/org/redisson/RedissonObject.java @@ -27,6 +27,7 @@ import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.pubsub.PubSubType; import org.redisson.command.BatchService; import org.redisson.command.CommandAsyncExecutor; +import org.redisson.api.ObjectEncoding; import org.redisson.connection.ServiceManager; import org.redisson.misc.CompletableFutureWrapper; import org.redisson.misc.Hash; @@ -606,11 +607,41 @@ public abstract class RedissonObject implements RObject { return get(getIdleTimeAsync()); } + @Override + public int getReferenceCount() { + return get(getReferenceCountAsync()); + } + + @Override + public int getAccessFrequency() { + return get(getAccessFrequencyAsync()); + } + + @Override + public ObjectEncoding getInternalEncoding() { + return get(getInternalEncodingAsync()); + } + @Override public RFuture getIdleTimeAsync() { return commandExecutor.writeAsync(getRawName(), StringCodec.INSTANCE, RedisCommands.OBJECT_IDLETIME, getRawName()); } + @Override + public RFuture getReferenceCountAsync() { + return commandExecutor.readAsync(getRawName(), StringCodec.INSTANCE, RedisCommands.OBJECT_REFCOUNT, getRawName()); + } + + @Override + public RFuture getAccessFrequencyAsync() { + return commandExecutor.readAsync(getRawName(), StringCodec.INSTANCE, RedisCommands.OBJECT_FREQ, getRawName()); + } + + @Override + public RFuture getInternalEncodingAsync() { + return commandExecutor.readAsync(getRawName(), StringCodec.INSTANCE, RedisCommands.OBJECT_ENCODING, getRawName()); + } + protected final void removeListener(int listenerId, String... names) { for (String name : names) { RPatternTopic topic = new RedissonPatternTopic(StringCodec.INSTANCE, commandExecutor, name); diff --git a/redisson/src/main/java/org/redisson/RedissonSearch.java b/redisson/src/main/java/org/redisson/RedissonSearch.java index 530c484ec..cf056f024 100644 --- a/redisson/src/main/java/org/redisson/RedissonSearch.java +++ b/redisson/src/main/java/org/redisson/RedissonSearch.java @@ -495,6 +495,9 @@ public class RedissonSearch implements RSearch { args.add(options.getCount()); } if (!options.getParams().isEmpty()) { + if (options.getDialect() == null || options.getDialect() < 2) { + throw new IllegalArgumentException("When use 'PARAMS', you should set DIALECT to 2 or greater than 2."); + } args.add("PARAMS"); args.add(options.getParams().size()*2); for (Map.Entry entry : options.getParams().entrySet()) { diff --git a/redisson/src/main/java/org/redisson/api/ObjectEncoding.java b/redisson/src/main/java/org/redisson/api/ObjectEncoding.java new file mode 100644 index 000000000..248907333 --- /dev/null +++ b/redisson/src/main/java/org/redisson/api/ObjectEncoding.java @@ -0,0 +1,119 @@ +/** + * 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.api; + +/** + * enum type from https://redis.io/docs/latest/commands/object-encoding/ + * + * @author seakider + */ +public enum ObjectEncoding { + /** + * Normal string encoding. + */ + RAW("raw"), + + /** + * Strings representing integers in a 64-bit signed interval. + */ + INT("int"), + + /** + * Strings with lengths up to the hardcoded limit of OBJ_ENCODING_EMBSTR_SIZE_LIMIT or 44 bytes. + */ + EMBSTR("embstr"), + + /** + * An old list encoding. + * No longer used. + */ + LINKEDLIST("linkedlist"), + + /** + * A space-efficient encoding used for small lists. + * Redis <= 6.2 + */ + ZIPLIST("ziplist"), + + /** + * A space-efficient encoding used for small lists. + * Redis >= 7.0 + */ + LISTPACK("listpack"), + + /** + * Encoded as linkedlist of ziplists or listpacks. + */ + QUICKLIST("quicklist"), + + /** + * Normal set encoding. + */ + HASHTABLE("hashtable"), + + /** + * Small sets composed solely of integers encoding. + */ + INTSET("intset"), + + /** + * An old hash encoding. + * No longer used + */ + ZIPMAP("zipmap"), + + /** + * Normal sorted set encoding + */ + SKIPLIST("skiplist"), + + /** + * Encoded as a radix tree of listpacks + */ + STREAM("stream"), + + /** + * Key is not exist. + */ + NULL("nonexistence"), + + /** + * This means redis support new type and this Enum not defined. + */ + UNKNOWN("unknown"); + + private final String type; + + ObjectEncoding(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public static ObjectEncoding valueOfEncoding(Object object) { + if (object == null) { + return NULL; + } + String value = (String) object; + for (ObjectEncoding encoding : ObjectEncoding.values()) { + if (value.equals(encoding.getType())) + return encoding; + } + return UNKNOWN; + } +} diff --git a/redisson/src/main/java/org/redisson/api/RObject.java b/redisson/src/main/java/org/redisson/api/RObject.java index 6bab032fa..567d7953d 100644 --- a/redisson/src/main/java/org/redisson/api/RObject.java +++ b/redisson/src/main/java/org/redisson/api/RObject.java @@ -34,6 +34,26 @@ public interface RObject extends RObjectAsync { */ Long getIdleTime(); + /** + * Returns count of references over this object. + * + * @return count of reference + */ + int getReferenceCount(); + + /** + * Returns the logarithmic access frequency counter over this object. + * + * @return frequency counter + */ + int getAccessFrequency(); + + /** + * Returns the internal encoding for the Redis object + * + * @return internal encoding + */ + ObjectEncoding getInternalEncoding(); /** * Returns bytes amount used by object in Redis memory. * diff --git a/redisson/src/main/java/org/redisson/api/RObjectAsync.java b/redisson/src/main/java/org/redisson/api/RObjectAsync.java index 6903974d9..05d9ea460 100644 --- a/redisson/src/main/java/org/redisson/api/RObjectAsync.java +++ b/redisson/src/main/java/org/redisson/api/RObjectAsync.java @@ -32,6 +32,27 @@ public interface RObjectAsync { */ RFuture getIdleTimeAsync(); + /** + * Returns count of references over this object. + * + * @return count of reference + */ + RFuture getReferenceCountAsync(); + + /** + * Returns the logarithmic access frequency counter over this object. + * + * @return frequency counter + */ + RFuture getAccessFrequencyAsync(); + + /** + * Returns the internal encoding for the Redis object + * + * @return internal encoding + */ + RFuture getInternalEncodingAsync(); + /** * Returns bytes amount used by object in Redis memory. * diff --git a/redisson/src/main/java/org/redisson/api/RObjectReactive.java b/redisson/src/main/java/org/redisson/api/RObjectReactive.java index c4f682ea5..644a7ef44 100644 --- a/redisson/src/main/java/org/redisson/api/RObjectReactive.java +++ b/redisson/src/main/java/org/redisson/api/RObjectReactive.java @@ -36,6 +36,27 @@ public interface RObjectReactive { */ Mono getIdleTime(); + /** + * Returns count of references over this object. + * + * @return count of reference + */ + Mono getReferenceCount(); + + /** + * Returns the logarithmic access frequency counter over this object. + * + * @return frequency counter + */ + Mono getAccessFrequency(); + + /** + * Returns the internal encoding for the Redis object + * + * @return internal encoding + */ + Mono getInternalEncoding(); + String getName(); Codec getCodec(); diff --git a/redisson/src/main/java/org/redisson/api/RObjectRx.java b/redisson/src/main/java/org/redisson/api/RObjectRx.java index 6a3e0a58e..c67309897 100644 --- a/redisson/src/main/java/org/redisson/api/RObjectRx.java +++ b/redisson/src/main/java/org/redisson/api/RObjectRx.java @@ -37,6 +37,27 @@ public interface RObjectRx { */ Single getIdleTime(); + /** + * Returns count of references over this object. + * + * @return count of reference + */ + Single getReferenceCount(); + + /** + * Returns the logarithmic access frequency counter over this object. + * + * @return frequency counter + */ + Single getAccessFrequency(); + + /** + * Returns the internal encoding for the Redis object + * + * @return internal encoding + */ + Single getInternalEncoding(); + String getName(); Codec getCodec(); diff --git a/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java b/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java index b13a1b395..bf3620ba4 100644 --- a/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -26,6 +26,7 @@ import org.redisson.client.protocol.decoder.*; import org.redisson.client.protocol.pubsub.PubSubStatusDecoder; import org.redisson.cluster.ClusterNodeInfo; import org.redisson.codec.CompositeCodec; +import org.redisson.api.ObjectEncoding; import java.time.Duration; import java.time.Instant; @@ -733,6 +734,15 @@ public interface RedisCommands { RedisStrictCommand NOT_EXISTS = new RedisStrictCommand("EXISTS", new BooleanNumberReplayConvertor(1L)); RedisStrictCommand OBJECT_IDLETIME = new RedisStrictCommand("OBJECT", "IDLETIME", new LongReplayConvertor()); + RedisStrictCommand OBJECT_REFCOUNT = new RedisStrictCommand("OBJECT", "REFCOUNT", new IntegerReplayConvertor(0)); + RedisStrictCommand OBJECT_FREQ = new RedisStrictCommand("OBJECT", "FREQ", new IntegerReplayConvertor(0)); + RedisStrictCommand OBJECT_ENCODING = new RedisStrictCommand<>("OBJECT", "ENCODING", new Convertor() { + @Override + public ObjectEncoding convert(Object obj) { + return ObjectEncoding.valueOfEncoding(obj); + } + }); + RedisStrictCommand MEMORY_USAGE = new RedisStrictCommand("MEMORY", "USAGE", new LongReplayConvertor()); RedisStrictCommand> MEMORY_STATS = new RedisStrictCommand<>("MEMORY", "STATS", new StringMapReplayDecoder()); RedisStrictCommand RENAMENX = new RedisStrictCommand("RENAMENX", new BooleanReplayConvertor()); diff --git a/redisson/src/test/java/org/redisson/RedisDockerTest.java b/redisson/src/test/java/org/redisson/RedisDockerTest.java index d5eb1dc10..0fd14499b 100644 --- a/redisson/src/test/java/org/redisson/RedisDockerTest.java +++ b/redisson/src/test/java/org/redisson/RedisDockerTest.java @@ -36,6 +36,8 @@ public class RedisDockerTest { protected static final String NOTIFY_KEYSPACE_EVENTS = "--notify-keyspace-events"; + protected static final String MAXMEMORY_POLICY = "--maxmemory-policy"; + protected static final GenericContainer REDIS = createRedis(); protected static final Protocol protocol = Protocol.RESP2; diff --git a/redisson/src/test/java/org/redisson/RedissonBucketTest.java b/redisson/src/test/java/org/redisson/RedissonBucketTest.java index f23503947..34ca1d345 100755 --- a/redisson/src/test/java/org/redisson/RedissonBucketTest.java +++ b/redisson/src/test/java/org/redisson/RedissonBucketTest.java @@ -6,14 +6,13 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.redisson.api.DeletedObjectListener; -import org.redisson.api.ExpiredObjectListener; -import org.redisson.api.RBucket; -import org.redisson.api.RedissonClient; +import org.redisson.api.*; import org.redisson.api.listener.SetObjectListener; import org.redisson.api.listener.TrackingListener; import org.redisson.api.options.PlainOptions; import org.redisson.client.RedisResponseTimeoutException; +import org.redisson.client.codec.IntegerCodec; +import org.redisson.client.codec.LongCodec; import org.redisson.client.codec.StringCodec; import org.redisson.config.Config; import org.redisson.config.Protocol; @@ -298,6 +297,58 @@ public class RedissonBucketTest extends RedisDockerTest { assertThat(al.getIdleTime()).isBetween(4L, 6L); } + @Test + public void testReferenceCount() { + RBucket al = redisson.getBucket("test"); + assertThat(al.getReferenceCount()).isEqualTo(0); + + al.set(10000); + assertThat(al.getReferenceCount()).isEqualTo(1); + } + + @Test + public void testAccessFrequency() { + testWithParams(redisson -> { + RBucket al = redisson.getBucket("test"); + assertThat(al.getAccessFrequency()).isEqualTo(0); + al.set(10000); + al.get(); + assertThat(al.getAccessFrequency()).isGreaterThan(1); + }, MAXMEMORY_POLICY, "allkeys-lfu"); + } + + @Test + public void testInternalEncoding() { + RBucket al = redisson.getBucket("test"); + assertThat(al.getInternalEncoding()).isEqualTo(ObjectEncoding.NULL); + al.set(123); + assertThat(al.getInternalEncoding()).isEqualTo(ObjectEncoding.EMBSTR); + + RList list=redisson.getList("list"); + list.addAll(Arrays.asList("a","b","c")); + assertThat(list.getInternalEncoding()).isEqualTo(ObjectEncoding.LISTPACK); + + RMap map = redisson.getMap("map"); + map.put(1, "12"); + map.put(2, "33"); + map.put(3, "43"); + assertThat(map.getInternalEncoding()).isEqualTo(ObjectEncoding.LISTPACK); + + RSet set = redisson.getSet("set", IntegerCodec.INSTANCE); + set.add(1); + set.add(2); + set.add(3); + assertThat(set.getInternalEncoding()).isEqualTo(ObjectEncoding.INTSET); + + RSortedSet sortedSet = redisson.getSortedSet("sortedSet", LongCodec.INSTANCE); + sortedSet.add(2L); + sortedSet.add(0L); + sortedSet.add(1L); + sortedSet.add(5L); + assertThat(sortedSet.getInternalEncoding()).isEqualTo(ObjectEncoding.LISTPACK); + + } + @Test public void testDeletedListener() { testWithParams(redisson -> { diff --git a/redisson/src/test/java/org/redisson/RedissonSearchTest.java b/redisson/src/test/java/org/redisson/RedissonSearchTest.java index cb70e5cac..0c8d16904 100644 --- a/redisson/src/test/java/org/redisson/RedissonSearchTest.java +++ b/redisson/src/test/java/org/redisson/RedissonSearchTest.java @@ -1,6 +1,7 @@ package org.redisson; import com.fasterxml.jackson.core.type.TypeReference; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.redisson.api.RJsonBucket; import org.redisson.api.RMap; @@ -48,7 +49,20 @@ public class RedissonSearchTest extends DockerRedisStackTest { return Objects.hash(name); } } - + + @Test + public void testSearchWithParam() { + RJsonBucket b = redisson.getJsonBucket("doc:1", StringCodec.INSTANCE); + b.set("[{\"arr\": [1, 2, 3]}, {\"val\": \"hello\"}, {\"val\": \"world\"}]"); + + RSearch s = redisson.getSearch(StringCodec.INSTANCE); + Assertions.assertThrows(IllegalArgumentException.class, () -> + s.search("idx", "*", QueryOptions.defaults() + .returnAttributes(new ReturnAttribute("arr"), + new ReturnAttribute("val")) + .params(Collections.singletonMap("12", "323")))); + } + @Test public void testSearchNoContent() { RMap m = redisson.getMap("doc:1", new CompositeCodec(StringCodec.INSTANCE, redisson.getConfig().getCodec()));