Feature - remainTimeToLive(Set keys) and clearExpire(Set keys) methods added to RMapCacheNative

Fixed - RMapCacheNative.clearExpire() method doesn't work
pull/6047/head
Nikita Koksharov 7 months ago
parent 29ee9500aa
commit b6174b05a8

@ -20,8 +20,10 @@ import org.redisson.api.listener.MapExpiredListener;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.codec.StringCodec;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.connection.decoder.MapNativeAllDecoder;
import org.redisson.misc.CompletableFutureWrapper;
import java.time.Duration;
@ -245,6 +247,26 @@ public class RedissonMapCacheNative<K, V> extends RedissonMap<K, V> implements R
return commandExecutor.readAsync(name, StringCodec.INSTANCE, RedisCommands.HPTTL, name, "FIELDS", 1, encodeMapKey(key));
}
@Override
public Map<K, Long> remainTimeToLive(Set<K> keys) {
return get(remainTimeToLiveAsync(keys));
}
@Override
public RFuture<Map<K, Long>> remainTimeToLiveAsync(Set<K> keys) {
List<Object> plainKeys = new ArrayList<>(keys);
List<Object> params = new ArrayList<>(keys.size() + 1);
params.add(getRawName());
params.add("FIELDS");
params.add(plainKeys.size());
encodeMapKeys(params, plainKeys);
RedisCommand<Map<Object, Object>> command = new RedisCommand<>("HPTTL",
new MapNativeAllDecoder(plainKeys, Long.class));
return commandExecutor.readAsync(getRawName(), StringCodec.INSTANCE, command, params.toArray());
}
@Override
public void putAll(Map<? extends K, ? extends V> map, Duration ttl) {
get(putAllAsync(map, ttl));
@ -412,14 +434,34 @@ public class RedissonMapCacheNative<K, V> extends RedissonMap<K, V> implements R
}
@Override
public boolean clearExpire(K key) {
public Boolean clearExpire(K key) {
return get(clearExpireAsync(key));
}
@Override
public RFuture<Boolean> clearExpireAsync(K key) {
String name = getRawName(key);
return commandExecutor.writeAsync(name, StringCodec.INSTANCE, RedisCommands.HPERSIST, name, encodeMapKey(key));
return commandExecutor.writeAsync(name, LongCodec.INSTANCE, RedisCommands.HPERSIST, name, "FIELDS", 1, encodeMapKey(key));
}
@Override
public Map<K, Boolean> clearExpire(Set<K> keys) {
return get(clearExpireAsync(keys));
}
@Override
public RFuture<Map<K, Boolean>> clearExpireAsync(Set<K> keys) {
List<Object> plainKeys = new ArrayList<>(keys);
List<Object> params = new ArrayList<>(keys.size() + 1);
params.add(getRawName());
params.add("FIELDS");
params.add(plainKeys.size());
encodeMapKeys(params, plainKeys);
RedisCommand<Map<Object, Object>> command = new RedisCommand<>("HPERSIST",
new MapNativeAllDecoder(plainKeys, Boolean.class));
return commandExecutor.readAsync(getRawName(), StringCodec.INSTANCE, command, params.toArray());
}
@Override

@ -18,6 +18,7 @@ package org.redisson.api;
import org.redisson.api.map.MapWriter;
import java.time.Duration;
import java.util.Map;
import java.util.Set;
/**
@ -107,13 +108,24 @@ public interface RMapCacheNative<K, V> extends RMap<K, V>, RMapCacheNativeAsync<
/**
* Remaining time to live of map entry associated with a <code>key</code>.
*
* @param key - map key
* @param key map key
* @return time in milliseconds
* -2 if the key does not exist.
* -1 if the key exists but has no associated expire.
*/
long remainTimeToLive(K key);
/**
* Remaining time to live of map entries associated with <code>keys</code>.
*
* @param keys map keys
* @return Time to live mapped by key.
* Time in milliseconds
* -2 if the key does not exist.
* -1 if the key exists but has no associated expire.
*/
Map<K, Long> remainTimeToLive(Set<K> keys);
/**
* Associates the specified <code>value</code> with the specified <code>key</code>
* in batch.
@ -127,13 +139,25 @@ public interface RMapCacheNative<K, V> extends RMap<K, V>, RMapCacheNativeAsync<
void putAll(java.util.Map<? extends K, ? extends V> map, Duration ttl);
/**
* Clear an expire timeout or expire date of specified entry by key.
* Clears an expiration timeout or date of specified entry by key.
*
* @param key map key
* @return <code>true</code> if timeout was removed
* <code>false</code> if object does not exist or does not have an associated timeout
* <code>false</code> if entry does not have an associated timeout
* <code>null</code> if entry does not exist
*/
Boolean clearExpire(K key);
/**
* Clears an expiration timeout or date of specified entries by keys.
*
* @param keys map keys
* @return Boolean mapped by key.
* <code>true</code> if timeout was removed
* <code>false</code> if entry does not have an associated timeout
* <code>null</code> if entry does not exist
*/
boolean clearExpire(K key);
Map<K, Boolean> clearExpire(Set<K> keys);
/**
* Updates time to live of specified entry by key.

@ -18,6 +18,7 @@ package org.redisson.api;
import org.redisson.api.map.MapWriter;
import java.time.Duration;
import java.util.Map;
import java.util.Set;
/**
@ -114,6 +115,8 @@ public interface RMapCacheNativeAsync<K, V> extends RMapAsync<K, V> {
*/
RFuture<Long> remainTimeToLiveAsync(K key);
RFuture<Map<K, Long>> remainTimeToLiveAsync(Set<K> keys);
/**
* Associates the specified <code>value</code> with the specified <code>key</code>
* in batch.
@ -127,14 +130,26 @@ public interface RMapCacheNativeAsync<K, V> extends RMapAsync<K, V> {
RFuture<Void> putAllAsync(java.util.Map<? extends K, ? extends V> map, Duration ttl);
/**
* Clear an expire timeout or expire date of specified entry by key.
* Clears an expiration timeout or date of specified entry by key.
*
* @param key map key
* @return <code>true</code> if timeout was removed
* <code>false</code> if object does not exist or does not have an associated timeout
* <code>false</code> if entry does not have an associated timeout
* <code>null</code> if entry does not exist
*/
RFuture<Boolean> clearExpireAsync(K key);
/**
* Clears an expiration timeout or date of specified entries by keys.
*
* @param keys map keys
* @return Boolean mapped by key.
* <code>true</code> if timeout was removed
* <code>false</code> if entry does not have an associated timeout
* <code>null</code> if entry does not exist
*/
RFuture<Map<K, Boolean>> clearExpireAsync(Set<K> keys);
/**
* Updates time to live and max idle time of specified entry by key.
* Entry expires when specified time to live was reached.

@ -19,6 +19,7 @@ import org.redisson.api.map.MapWriter;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Map;
import java.util.Set;
/**
@ -107,13 +108,24 @@ public interface RMapCacheNativeReactive<K, V> extends RMapReactive<K, V>, RDest
/**
* Remaining time to live of map entry associated with a <code>key</code>.
*
* @param key - map key
* @param key map key
* @return time in milliseconds
* -2 if the key does not exist.
* -1 if the key exists but has no associated expire.
*/
Mono<Long> remainTimeToLive(K key);
/**
* Remaining time to live of map entries associated with <code>keys</code>.
*
* @param keys map keys
* @return Time to live mapped by key.
* Time in milliseconds
* -2 if the key does not exist.
* -1 if the key exists but has no associated expire.
*/
Mono<Map<K, Long>> remainTimeToLive(Set<K> keys);
/**
* Associates the specified <code>value</code> with the specified <code>key</code>
* in batch.
@ -127,14 +139,26 @@ public interface RMapCacheNativeReactive<K, V> extends RMapReactive<K, V>, RDest
Mono<Void> putAll(java.util.Map<? extends K, ? extends V> map, Duration ttl);
/**
* Clear an expire timeout or expire date of specified entry by key.
* Clears an expiration timeout or date of specified entry by key.
*
* @param key map key
* @return <code>true</code> if timeout was removed
* <code>false</code> if object does not exist or does not have an associated timeout
* <code>false</code> if entry does not have an associated timeout
* <code>null</code> if entry does not exist
*/
Mono<Boolean> clearExpire(K key);
/**
* Clears an expiration timeout or date of specified entries by keys.
*
* @param keys map keys
* @return Boolean mapped by key.
* <code>true</code> if timeout was removed
* <code>false</code> if entry does not have an associated timeout
* <code>null</code> if entry does not exist
*/
Mono<Map<K, Boolean>> clearExpire(Set<K> keys);
/**
* Updates time to live of specified entry by key.
* Entry expires when specified time to live was reached.

@ -21,6 +21,7 @@ import io.reactivex.rxjava3.core.Single;
import org.redisson.api.map.MapWriter;
import java.time.Duration;
import java.util.Map;
import java.util.Set;
/**
@ -110,13 +111,24 @@ public interface RMapCacheNativeRx<K, V> extends RMapRx<K, V>, RDestroyable {
/**
* Remaining time to live of map entry associated with a <code>key</code>.
*
* @param key - map key
* @param key map key
* @return time in milliseconds
* -2 if the key does not exist.
* -1 if the key exists but has no associated expire.
*/
Single<Long> remainTimeToLive(K key);
/**
* Remaining time to live of map entries associated with <code>keys</code>.
*
* @param keys map keys
* @return Time to live mapped by key.
* Time in milliseconds
* -2 if the key does not exist.
* -1 if the key exists but has no associated expire.
*/
Single<Map<K, Long>> remainTimeToLive(Set<K> keys);
/**
* Associates the specified <code>value</code> with the specified <code>key</code>
* in batch.
@ -130,13 +142,25 @@ public interface RMapCacheNativeRx<K, V> extends RMapRx<K, V>, RDestroyable {
Completable putAll(java.util.Map<? extends K, ? extends V> map, Duration ttl);
/**
* Clear an expire timeout or expire date of specified entry by key.
* Clears an expiration timeout or date of specified entry by key.
*
* @param key map key
* @return <code>true</code> if timeout was removed
* <code>false</code> if object does not exist or does not have an associated timeout
* <code>false</code> if entry does not have an associated timeout
* <code>null</code> if entry does not exist
*/
Maybe<Boolean> clearExpire(K key);
/**
* Clears an expiration timeout or date of specified entries by keys.
*
* @param keys map keys
* @return Boolean mapped by key.
* <code>true</code> if timeout was removed
* <code>false</code> if entry does not have an associated timeout
* <code>null</code> if entry does not exist
*/
Single<Boolean> clearExpire(K key);
Single<Map<K, Boolean>> clearExpire(Set<K> keys);
/**
* Updates time to live of specified entry by key.

@ -506,7 +506,17 @@ public interface RedisCommands {
RedisStrictCommand<Void> MSET = new RedisStrictCommand<Void>("MSET", new VoidReplayConvertor());
RedisStrictCommand<Boolean> MSETNX = new RedisStrictCommand<Boolean>("MSETNX", new BooleanReplayConvertor());
RedisStrictCommand<Boolean> HPERSIST = new RedisStrictCommand<Boolean>("HPERSIST", new BooleanReplayConvertor());
RedisCommand<Boolean> HPERSIST = new RedisCommand("HPERSIST", new ListFirstObjectDecoder(), new Convertor<Boolean>() {
@Override
public Boolean convert(Object obj) {
Long val = (Long) obj;
if (val == -2) {
return null;
}
return val == 1;
}
});
RedisCommand<Long> HPTTL = new RedisCommand("HPTTL", new ListFirstObjectDecoder(), new LongReplayConvertor());
RedisStrictCommand<Boolean> HSETNX = new RedisStrictCommand<Boolean>("HSETNX", new BooleanReplayConvertor());
RedisStrictCommand<Boolean> HSET = new RedisStrictCommand<Boolean>("HSET", new BooleanReplayConvertor());

@ -0,0 +1,61 @@
/**
* 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.connection.decoder;
import org.redisson.client.handler.State;
import org.redisson.client.protocol.decoder.MultiDecoder;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
*
* @author Nikita Koksharov
*
*/
public class MapNativeAllDecoder implements MultiDecoder<Map<Object, Object>> {
private final List<Object> args;
private final Class<?> valueClass;
public MapNativeAllDecoder(List<Object> args, Class<?> valueClass) {
this.args = args;
this.valueClass = valueClass;
}
@Override
public Map<Object, Object> decode(List<Object> parts, State state) {
if (parts.isEmpty()) {
return new HashMap<>();
}
Map<Object, Object> result = new LinkedHashMap<>(parts.size());
for (int index = 0; index < parts.size(); index++) {
Long value = (Long) parts.get(index);
if (value == -2 && valueClass != Long.class) {
continue;
}
if (valueClass == Boolean.class) {
result.put(args.get(index), value == 1);
} else {
result.put(args.get(index), value);
}
}
return result;
}
}

@ -216,7 +216,13 @@ public class RedissonMapCacheNativeTest extends BaseMapTest {
map.put("5", "6", Duration.ofSeconds(20));
assertThat(map.remainTimeToLive("1")).isLessThan(9900);
map.destroy();
Map<String, Long> r = map.remainTimeToLive(Set.of("0", "1", "3", "5", "6"));
assertThat(r.get("0")).isEqualTo(-2);
assertThat(r.get("1")).isGreaterThan(1);
assertThat(r.get("3")).isEqualTo(-1);
assertThat(r.get("5")).isGreaterThan(1);
assertThat(r.get("6")).isEqualTo(-2);
}
@Test
@ -384,6 +390,23 @@ public class RedissonMapCacheNativeTest extends BaseMapTest {
Assertions.assertEquals(0, cache.size());
cache.destroy();
}
@Test
public void testClear() {
RMapCacheNative<String, String> cache = redisson.getMapCacheNative("simple");
cache.put("0", "8", Duration.ofSeconds(1));
cache.put("02", "18", Duration.ofSeconds(1));
cache.put("03", "38", Duration.ofSeconds(1));
assertThat(cache.clearExpire("0")).isTrue();
assertThat(cache.clearExpire("01")).isNull();
Map<String, Boolean> r = cache.clearExpire(Set.of("0", "02", "03", "04"));
assertThat(r.get("0")).isFalse();
assertThat(r.get("02")).isTrue();
assertThat(r.get("03")).isTrue();
assertThat(r.get("04")).isNull();
}
@Test
public void testClearExpire() throws InterruptedException {

Loading…
Cancel
Save