diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java index f2c6c625ce..8b09c6b2e2 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java @@ -19,15 +19,6 @@ */ package io.lettuce.core; -import static io.lettuce.core.protocol.CommandType.*; - -import java.time.Duration; -import java.time.Instant; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; - import io.lettuce.core.GeoArgs.Unit; import io.lettuce.core.api.StatefulConnection; import io.lettuce.core.api.async.*; @@ -50,6 +41,19 @@ import io.lettuce.core.protocol.ProtocolKeyword; import io.lettuce.core.protocol.RedisCommand; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static io.lettuce.core.protocol.CommandType.EXEC; +import static io.lettuce.core.protocol.CommandType.GEORADIUS; +import static io.lettuce.core.protocol.CommandType.GEORADIUSBYMEMBER; +import static io.lettuce.core.protocol.CommandType.GEORADIUSBYMEMBER_RO; +import static io.lettuce.core.protocol.CommandType.GEORADIUS_RO; + /** * An asynchronous and thread-safe API for a Redis connection. * @@ -794,6 +798,27 @@ public RedisFuture<Boolean> expire(K key, Duration seconds, ExpireArgs expireArg return expire(key, seconds.toMillis() / 1000, expireArgs); } + @Override + public RedisFuture<Boolean> hexpire(K key, long seconds, K... fields) { + return hexpire(key, seconds, null, fields); + } + + @Override + public RedisFuture<Boolean> hexpire(K key, long seconds, ExpireArgs expireArgs, K... fields) { + return dispatch(commandBuilder.hexpire(key, seconds, expireArgs, fields)); + } + + @Override + public RedisFuture<Boolean> hexpire(K key, Duration seconds, K... fields) { + return hexpire(key, seconds, null, fields); + } + + @Override + public RedisFuture<Boolean> hexpire(K key, Duration seconds, ExpireArgs expireArgs, K... fields) { + LettuceAssert.notNull(seconds, "Timeout must not be null"); + return hexpire(key, seconds.toMillis() / 1000, expireArgs, fields); + } + @Override public RedisFuture<Boolean> expireat(K key, long timestamp) { return expireat(key, timestamp, null); @@ -826,11 +851,49 @@ public RedisFuture<Boolean> expireat(K key, Instant timestamp, ExpireArgs expire return expireat(key, timestamp.toEpochMilli() / 1000, expireArgs); } + @Override + public RedisFuture<Boolean> hexpireat(K key, long timestamp, K... fields) { + return hexpireat(key, timestamp, null, fields); + } + + @Override + public RedisFuture<Boolean> hexpireat(K key, long timestamp, ExpireArgs expireArgs, K... fields) { + return dispatch(commandBuilder.hexpireat(key, timestamp, expireArgs, fields)); + + } + + @Override + public RedisFuture<Boolean> hexpireat(K key, Date timestamp, K... fields) { + return hexpireat(key, timestamp, null, fields); + } + + @Override + public RedisFuture<Boolean> hexpireat(K key, Date timestamp, ExpireArgs expireArgs, K... fields) { + LettuceAssert.notNull(timestamp, "Timestamp must not be null"); + return hexpireat(key, timestamp.getTime() / 1000, expireArgs, fields); + } + + @Override + public RedisFuture<Boolean> hexpireat(K key, Instant timestamp, K... fields) { + return hexpireat(key, timestamp, null, fields); + } + + @Override + public RedisFuture<Boolean> hexpireat(K key, Instant timestamp, ExpireArgs expireArgs, K... fields) { + LettuceAssert.notNull(timestamp, "Timestamp must not be null"); + return hexpireat(key, timestamp.toEpochMilli() / 1000, expireArgs, fields); + } + @Override public RedisFuture<Long> expiretime(K key) { return dispatch(commandBuilder.expiretime(key)); } + @Override + public RedisFuture<Long> hexpiretime(K key, K... fields) { + return dispatch(commandBuilder.hexpiretime(key, fields)); + } + @Override public <T> RedisFuture<T> fcall(String function, ScriptOutputType type, K... keys) { return dispatch(commandBuilder.fcall(function, type, false, keys)); @@ -1489,6 +1552,11 @@ public RedisFuture<Boolean> persist(K key) { return dispatch(commandBuilder.persist(key)); } + @Override + public RedisFuture<Boolean> hpersist(K key, K... fields) { + return dispatch(commandBuilder.hpersist(key, fields)); + } + @Override public RedisFuture<Boolean> pexpire(K key, long milliseconds) { return pexpire(key, milliseconds, null); diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java index 47b473495e..47006ac03d 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java @@ -19,16 +19,6 @@ */ package io.lettuce.core; -import static io.lettuce.core.protocol.CommandType.*; - -import java.time.Duration; -import java.time.Instant; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Supplier; - import io.lettuce.core.GeoArgs.Unit; import io.lettuce.core.api.StatefulConnection; import io.lettuce.core.api.reactive.*; @@ -59,6 +49,20 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +import static io.lettuce.core.protocol.CommandType.EXEC; +import static io.lettuce.core.protocol.CommandType.GEORADIUS; +import static io.lettuce.core.protocol.CommandType.GEORADIUSBYMEMBER; +import static io.lettuce.core.protocol.CommandType.GEORADIUSBYMEMBER_RO; +import static io.lettuce.core.protocol.CommandType.GEORADIUS_RO; + /** * A reactive and thread-safe API for a Redis connection. * @@ -854,6 +858,27 @@ public Mono<Boolean> expire(K key, Duration seconds, ExpireArgs expireArgs) { return expire(key, seconds.toMillis() / 1000, expireArgs); } + @Override + public Mono<Boolean> hexpire(K key, long seconds, K... fields) { + return hexpire(key, seconds, null, fields); + } + + @Override + public Mono<Boolean> hexpire(K key, long seconds, ExpireArgs expireArgs, K... fields) { + return createMono(() -> commandBuilder.hexpire(key, seconds, expireArgs, fields)); + } + + @Override + public Mono<Boolean> hexpire(K key, Duration seconds, K... fields) { + return hexpire(key, seconds, null, fields); + } + + @Override + public Mono<Boolean> hexpire(K key, Duration seconds, ExpireArgs expireArgs, K... fields) { + LettuceAssert.notNull(seconds, "Timeout must not be null"); + return hexpire(key, seconds.toMillis() / 1000, expireArgs, fields); + } + @Override public Mono<Boolean> expireat(K key, long timestamp) { return expireat(key, timestamp, null); @@ -886,11 +911,48 @@ public Mono<Boolean> expireat(K key, Instant timestamp, ExpireArgs expireArgs) { return expireat(key, timestamp.toEpochMilli() / 1000, expireArgs); } + @Override + public Mono<Boolean> hexpireat(K key, long timestamp, K... fields) { + return hexpireat(key, timestamp, null, fields); + } + + @Override + public Mono<Boolean> hexpireat(K key, long timestamp, ExpireArgs expireArgs, K... fields) { + return createMono(() -> commandBuilder.hexpireat(key, timestamp, expireArgs, fields)); + } + + @Override + public Mono<Boolean> hexpireat(K key, Date timestamp, K... fields) { + return hexpireat(key, timestamp, null, fields); + } + + @Override + public Mono<Boolean> hexpireat(K key, Date timestamp, ExpireArgs expireArgs, K... fields) { + LettuceAssert.notNull(timestamp, "Timestamp must not be null"); + return hexpireat(key, timestamp.getTime() / 1000, expireArgs, fields); + } + + @Override + public Mono<Boolean> hexpireat(K key, Instant timestamp, K... fields) { + return hexpireat(key, timestamp, null, fields); + } + + @Override + public Mono<Boolean> hexpireat(K key, Instant timestamp, ExpireArgs expireArgs, K... fields) { + LettuceAssert.notNull(timestamp, "Timestamp must not be null"); + return hexpireat(key, timestamp.toEpochMilli() / 1000, expireArgs, fields); + } + @Override public Mono<Long> expiretime(K key) { return createMono(() -> commandBuilder.expiretime(key)); } + @Override + public Mono<Long> hexpiretime(K key, K... fields) { + return createMono(() -> commandBuilder.hexpiretime(key, fields)); + } + @Override public <T> Flux<T> fcall(String function, ScriptOutputType type, K... keys) { return createFlux(() -> commandBuilder.fcall(function, type, false, keys)); @@ -1556,6 +1618,11 @@ public Mono<Boolean> persist(K key) { return createMono(() -> commandBuilder.persist(key)); } + @Override + public Mono<Boolean> hpersist(K key, K... fields) { + return createMono(() -> commandBuilder.hpersist(key, fields)); + } + @Override public Mono<Boolean> pexpire(K key, long milliseconds) { return pexpire(key, milliseconds, null); diff --git a/src/main/java/io/lettuce/core/RedisCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCommandBuilder.java index 3d3d2be0be..143fc6cd89 100644 --- a/src/main/java/io/lettuce/core/RedisCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisCommandBuilder.java @@ -19,19 +19,6 @@ */ package io.lettuce.core; -import static io.lettuce.core.internal.LettuceStrings.*; -import static io.lettuce.core.protocol.CommandKeyword.*; -import static io.lettuce.core.protocol.CommandType.*; -import static io.lettuce.core.protocol.CommandType.COPY; -import static io.lettuce.core.protocol.CommandType.SAVE; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; - import io.lettuce.core.Range.Boundary; import io.lettuce.core.XReadArgs.StreamOffset; import io.lettuce.core.codec.RedisCodec; @@ -48,6 +35,19 @@ import io.lettuce.core.protocol.CommandType; import io.lettuce.core.protocol.RedisCommand; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static io.lettuce.core.internal.LettuceStrings.string; +import static io.lettuce.core.protocol.CommandKeyword.*; +import static io.lettuce.core.protocol.CommandType.*; +import static io.lettuce.core.protocol.CommandType.COPY; +import static io.lettuce.core.protocol.CommandType.SAVE; + /** * @param <K> * @param <V> @@ -978,6 +978,38 @@ Command<K, V, Boolean> expire(K key, long seconds, ExpireArgs expireArgs) { return createCommand(EXPIRE, new BooleanOutput<>(codec), args); } + Command<K, V, Boolean> hexpire(K key, long seconds, ExpireArgs expireArgs, K... fields) { + notNullKey(key); + notEmpty(fields); + + CommandArgs<K, V> args = new CommandArgs<>(codec).addKey(key).add(seconds); + + if (expireArgs != null) { + expireArgs.build(args); + } + + args.add(fields.length); + args.addKeys(fields); + + return createCommand(HEXPIRE, new BooleanOutput<>(codec), args); + } + + Command<K, V, Boolean> hexpireat(K key, long seconds, ExpireArgs expireArgs, K... fields) { + notNullKey(key); + notEmpty(fields); + + CommandArgs<K, V> args = new CommandArgs<>(codec).addKey(key).add(seconds); + + if (expireArgs != null) { + expireArgs.build(args); + } + + args.add(fields.length); + args.addKeys(fields); + + return createCommand(HEXPIREAT, new BooleanOutput<>(codec), args); + } + Command<K, V, Boolean> expireat(K key, long timestamp, ExpireArgs expireArgs) { notNullKey(key); @@ -997,6 +1029,15 @@ Command<K, V, Long> expiretime(K key) { return createCommand(EXPIRETIME, new IntegerOutput<>(codec), args); } + Command<K, V, Long> hexpiretime(K key, K... fields) { + notNullKey(key); + + CommandArgs<K, V> args = new CommandArgs<>(codec).addKey(key); + args.add(fields.length); + args.addKeys(fields); + return createCommand(HEXPIRETIME, new IntegerOutput<>(codec), args); + } + Command<K, V, String> flushall() { return createCommand(FLUSHALL, new StatusOutput<>(codec)); } @@ -2043,6 +2084,16 @@ Command<K, V, Boolean> persist(K key) { return createCommand(PERSIST, new BooleanOutput<>(codec), key); } + Command<K, V, Boolean> hpersist(K key, K... fields) { + notNullKey(key); + + CommandArgs<K, V> args = new CommandArgs<>(codec).addKey(key); + args.add(fields.length); + args.addKeys(fields); + + return createCommand(HPERSIST, new BooleanOutput<>(codec), args); + } + Command<K, V, Boolean> pexpire(K key, long milliseconds, ExpireArgs expireArgs) { notNullKey(key); diff --git a/src/main/java/io/lettuce/core/api/async/RedisHashAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisHashAsyncCommands.java index cc9b8b1489..9f514450fa 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisHashAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisHashAsyncCommands.java @@ -19,9 +19,7 @@ */ package io.lettuce.core.api.async; -import java.util.List; -import java.util.Map; - +import io.lettuce.core.ExpireArgs; import io.lettuce.core.KeyScanCursor; import io.lettuce.core.KeyValue; import io.lettuce.core.MapScanCursor; @@ -33,6 +31,12 @@ import io.lettuce.core.output.KeyValueStreamingChannel; import io.lettuce.core.output.ValueStreamingChannel; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Map; + /** * Asynchronous executed commands for Hashes (Key-Value pairs). * @@ -429,4 +433,152 @@ public interface RedisHashAsyncCommands<K, V> { * @return Long count of the keys. */ RedisFuture<Long> hvals(ValueStreamingChannel<V> channel, K key); + + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + RedisFuture<Boolean> hexpire(K key, long seconds, K... fields); + + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + RedisFuture<Boolean> hexpire(K key, long seconds, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL {@link Duration} + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + RedisFuture<Boolean> hexpire(K key, Duration seconds, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL {@link Duration} + * @param expireArgs the {@link ExpireArgs}. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + RedisFuture<Boolean> hexpire(K key, Duration seconds, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + RedisFuture<Boolean> hexpireat(K key, long timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + RedisFuture<Boolean> hexpireat(K key, long timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + RedisFuture<Boolean> hexpireat(K key, Date timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + RedisFuture<Boolean> hexpireat(K key, Date timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + RedisFuture<Boolean> hexpireat(K key, Instant timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + RedisFuture<Boolean> hexpireat(K key, Instant timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Get the time to live for one or more fields in as unix timestamp in seconds. + * + * @param key the key. + * @param fields one or more fields to get the TTL for. + * @return Long integer-reply in seconds, or a negative value in order to signal an error. The command returns {@code -1} if + * the key exists but has no associated expiration time. The command returns {@code -2} if the key does not exist. + * @since 7.0 + */ + RedisFuture<Long> hexpiretime(K key, K... fields); + + /** + * Remove the expiration from one or more fields. + * + * @param key the key. + * @param fields one or more fields to remove the TTL for. + * @return Boolean integer-reply specifically: + * + * {@code true} if the timeout was removed. {@code false} if {@code key} does not exist or does not have an + * associated timeout. + */ + RedisFuture<Boolean> hpersist(K key, K... fields); } diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisHashReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisHashReactiveCommands.java index 080087510a..e02fe5b80a 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisHashReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisHashReactiveCommands.java @@ -19,10 +19,7 @@ */ package io.lettuce.core.api.reactive; -import java.util.Map; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; +import io.lettuce.core.ExpireArgs; import io.lettuce.core.KeyScanCursor; import io.lettuce.core.KeyValue; import io.lettuce.core.MapScanCursor; @@ -32,6 +29,13 @@ import io.lettuce.core.output.KeyStreamingChannel; import io.lettuce.core.output.KeyValueStreamingChannel; import io.lettuce.core.output.ValueStreamingChannel; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.Map; /** * Reactive executed commands for Hashes (Key-Value pairs). @@ -451,4 +455,152 @@ public interface RedisHashReactiveCommands<K, V> { */ @Deprecated Mono<Long> hvals(ValueStreamingChannel<V> channel, K key); + + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Mono<Boolean> hexpire(K key, long seconds, K... fields); + + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Mono<Boolean> hexpire(K key, long seconds, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL {@link Duration} + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Mono<Boolean> hexpire(K key, Duration seconds, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL {@link Duration} + * @param expireArgs the {@link ExpireArgs}. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Mono<Boolean> hexpire(K key, Duration seconds, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Mono<Boolean> hexpireat(K key, long timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Mono<Boolean> hexpireat(K key, long timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Mono<Boolean> hexpireat(K key, Date timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Mono<Boolean> hexpireat(K key, Date timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Mono<Boolean> hexpireat(K key, Instant timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Mono<Boolean> hexpireat(K key, Instant timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Get the time to live for one or more fields in as unix timestamp in seconds. + * + * @param key the key. + * @param fields one or more fields to get the TTL for. + * @return Long integer-reply in seconds, or a negative value in order to signal an error. The command returns {@code -1} if + * the key exists but has no associated expiration time. The command returns {@code -2} if the key does not exist. + * @since 7.0 + */ + Mono<Long> hexpiretime(K key, K... fields); + + /** + * Remove the expiration from one or more fields. + * + * @param key the key. + * @param fields one or more fields to remove the TTL for. + * @return Boolean integer-reply specifically: + * + * {@code true} if the timeout was removed. {@code false} if {@code key} does not exist or does not have an + * associated timeout. + */ + Mono<Boolean> hpersist(K key, K... fields); } diff --git a/src/main/java/io/lettuce/core/api/sync/RedisHashCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisHashCommands.java index 816ec08227..ab41a1cafd 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisHashCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisHashCommands.java @@ -19,9 +19,7 @@ */ package io.lettuce.core.api.sync; -import java.util.List; -import java.util.Map; - +import io.lettuce.core.ExpireArgs; import io.lettuce.core.KeyScanCursor; import io.lettuce.core.KeyValue; import io.lettuce.core.MapScanCursor; @@ -32,6 +30,12 @@ import io.lettuce.core.output.KeyValueStreamingChannel; import io.lettuce.core.output.ValueStreamingChannel; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Map; + /** * Synchronous executed commands for Hashes (Key-Value pairs). * @@ -430,4 +434,152 @@ public interface RedisHashCommands<K, V> { * @return Long count of the keys. */ Long hvals(ValueStreamingChannel<V> channel, K key); + + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Boolean hexpire(K key, long seconds, K... fields); + + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Boolean hexpire(K key, long seconds, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL {@link Duration} + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Boolean hexpire(K key, Duration seconds, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL {@link Duration} + * @param expireArgs the {@link ExpireArgs}. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Boolean hexpire(K key, Duration seconds, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Boolean hexpireat(K key, long timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Boolean hexpireat(K key, long timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Boolean hexpireat(K key, Date timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Boolean hexpireat(K key, Date timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Boolean hexpireat(K key, Instant timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Boolean hexpireat(K key, Instant timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Get the time to live for one or more fields in as unix timestamp in seconds. + * + * @param key the key. + * @param fields one or more fields to get the TTL for. + * @return Long integer-reply in seconds, or a negative value in order to signal an error. The command returns {@code -1} if + * the key exists but has no associated expiration time. The command returns {@code -2} if the key does not exist. + * @since 7.0 + */ + Long hexpiretime(K key, K... fields); + + /** + * Remove the expiration from one or more fields. + * + * @param key the key. + * @param fields one or more fields to remove the TTL for. + * @return Boolean integer-reply specifically: + * + * {@code true} if the timeout was removed. {@code false} if {@code key} does not exist or does not have an + * associated timeout. + */ + Boolean hpersist(K key, K... fields); } diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionHashAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionHashAsyncCommands.java index dd99fd23b0..442444c6ed 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionHashAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionHashAsyncCommands.java @@ -19,9 +19,7 @@ */ package io.lettuce.core.cluster.api.async; -import java.util.List; -import java.util.Map; - +import io.lettuce.core.ExpireArgs; import io.lettuce.core.KeyScanCursor; import io.lettuce.core.KeyValue; import io.lettuce.core.MapScanCursor; @@ -32,6 +30,12 @@ import io.lettuce.core.output.KeyValueStreamingChannel; import io.lettuce.core.output.ValueStreamingChannel; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Map; + /** * Asynchronous executed commands on a node selection for Hashes (Key-Value pairs). * @@ -428,4 +432,152 @@ public interface NodeSelectionHashAsyncCommands<K, V> { * @return Long count of the keys. */ AsyncExecutions<Long> hvals(ValueStreamingChannel<V> channel, K key); + + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + AsyncExecutions<Boolean> hexpire(K key, long seconds, K... fields); + + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + AsyncExecutions<Boolean> hexpire(K key, long seconds, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL {@link Duration} + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + AsyncExecutions<Boolean> hexpire(K key, Duration seconds, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL {@link Duration} + * @param expireArgs the {@link ExpireArgs}. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + AsyncExecutions<Boolean> hexpire(K key, Duration seconds, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + AsyncExecutions<Boolean> hexpireat(K key, long timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + AsyncExecutions<Boolean> hexpireat(K key, long timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + AsyncExecutions<Boolean> hexpireat(K key, Date timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + AsyncExecutions<Boolean> hexpireat(K key, Date timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + AsyncExecutions<Boolean> hexpireat(K key, Instant timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + AsyncExecutions<Boolean> hexpireat(K key, Instant timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Get the time to live for one or more fields in as unix timestamp in seconds. + * + * @param key the key. + * @param fields one or more fields to get the TTL for. + * @return Long integer-reply in seconds, or a negative value in order to signal an error. The command returns {@code -1} if + * the key exists but has no associated expiration time. The command returns {@code -2} if the key does not exist. + * @since 7.0 + */ + AsyncExecutions<Long> hexpiretime(K key, K... fields); + + /** + * Remove the expiration from one or more fields. + * + * @param key the key. + * @param fields one or more fields to remove the TTL for. + * @return Boolean integer-reply specifically: + * + * {@code true} if the timeout was removed. {@code false} if {@code key} does not exist or does not have an + * associated timeout. + */ + AsyncExecutions<Boolean> hpersist(K key, K... fields); } diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionHashCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionHashCommands.java index 4d9a3c694b..f2aeaf52b4 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionHashCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionHashCommands.java @@ -19,9 +19,7 @@ */ package io.lettuce.core.cluster.api.sync; -import java.util.List; -import java.util.Map; - +import io.lettuce.core.ExpireArgs; import io.lettuce.core.KeyScanCursor; import io.lettuce.core.KeyValue; import io.lettuce.core.MapScanCursor; @@ -32,6 +30,12 @@ import io.lettuce.core.output.KeyValueStreamingChannel; import io.lettuce.core.output.ValueStreamingChannel; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Map; + /** * Synchronous executed commands on a node selection for Hashes (Key-Value pairs). * @@ -428,4 +432,152 @@ public interface NodeSelectionHashCommands<K, V> { * @return Long count of the keys. */ Executions<Long> hvals(ValueStreamingChannel<V> channel, K key); + + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Executions<Boolean> hexpire(K key, long seconds, K... fields); + + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Executions<Boolean> hexpire(K key, long seconds, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL {@link Duration} + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Executions<Boolean> hexpire(K key, Duration seconds, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL {@link Duration} + * @param expireArgs the {@link ExpireArgs}. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Executions<Boolean> hexpire(K key, Duration seconds, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Executions<Boolean> hexpireat(K key, long timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Executions<Boolean> hexpireat(K key, long timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Executions<Boolean> hexpireat(K key, Date timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Executions<Boolean> hexpireat(K key, Date timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Executions<Boolean> hexpireat(K key, Instant timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Executions<Boolean> hexpireat(K key, Instant timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Get the time to live for one or more fields in as unix timestamp in seconds. + * + * @param key the key. + * @param fields one or more fields to get the TTL for. + * @return Long integer-reply in seconds, or a negative value in order to signal an error. The command returns {@code -1} if + * the key exists but has no associated expiration time. The command returns {@code -2} if the key does not exist. + * @since 7.0 + */ + Executions<Long> hexpiretime(K key, K... fields); + + /** + * Remove the expiration from one or more fields. + * + * @param key the key. + * @param fields one or more fields to remove the TTL for. + * @return Boolean integer-reply specifically: + * + * {@code true} if the timeout was removed. {@code false} if {@code key} does not exist or does not have an + * associated timeout. + */ + Executions<Boolean> hpersist(K key, K... fields); } diff --git a/src/main/java/io/lettuce/core/protocol/CommandType.java b/src/main/java/io/lettuce/core/protocol/CommandType.java index 7a9d87524d..7207a50dab 100644 --- a/src/main/java/io/lettuce/core/protocol/CommandType.java +++ b/src/main/java/io/lettuce/core/protocol/CommandType.java @@ -46,7 +46,7 @@ public enum CommandType implements ProtocolKeyword { // Keys - COPY, DEL, DUMP, EXISTS, EXPIRE, EXPIREAT, EXPIRETIME, KEYS, MIGRATE, MOVE, OBJECT, PERSIST, PEXPIRE, PEXPIREAT, PEXPIRETIME, PTTL, RANDOMKEY, RENAME, RENAMENX, RESTORE, TOUCH, TTL, TYPE, SCAN, UNLINK, + COPY, DEL, DUMP, EXISTS, HEXPIRE, EXPIRE, HEXPIREAT, EXPIREAT, HEXPIRETIME, EXPIRETIME, KEYS, MIGRATE, MOVE, OBJECT, HPERSIST, PERSIST, PEXPIRE, PEXPIREAT, PEXPIRETIME, PTTL, RANDOMKEY, RENAME, RENAMENX, RESTORE, TOUCH, TTL, TYPE, SCAN, UNLINK, // String diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisHashCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisHashCoroutinesCommands.kt index 6ab7746601..34a4228f49 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisHashCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisHashCoroutinesCommands.kt @@ -20,8 +20,17 @@ package io.lettuce.core.api.coroutines -import io.lettuce.core.* +import io.lettuce.core.ExperimentalLettuceCoroutinesApi +import io.lettuce.core.ExpireArgs import kotlinx.coroutines.flow.Flow +import io.lettuce.core.KeyScanCursor +import io.lettuce.core.KeyValue +import io.lettuce.core.MapScanCursor +import io.lettuce.core.ScanArgs +import io.lettuce.core.ScanCursor +import java.time.Duration +import java.time.Instant +import java.util.* /** * Coroutine executed commands for Hashes (Key-Value pairs). @@ -298,5 +307,153 @@ interface RedisHashCoroutinesCommands<K : Any, V : Any> { */ fun hvals(key: K): Flow<V> + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not + * exist or the timeout could not be set. + * @since 7.0 + */ + suspend fun hexpire(key: K, seconds: Long, vararg fields: K): Boolean? + + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not + * exist or the timeout could not be set. + * @since 7.0 + */ + suspend fun hexpire(key: K, seconds: Long, expireArgs: ExpireArgs, vararg fields: K): Boolean? + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL [Duration] + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not + * exist or the timeout could not be set. + * @since 7.0 + */ + suspend fun hexpire(key: K, seconds: Duration, vararg fields: K): Boolean? + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL [Duration] + * @param expireArgs the [ExpireArgs]. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not + * exist or the timeout could not be set. + * @since 7.0 + */ + suspend fun hexpire(key: K, seconds: Duration, expireArgs: ExpireArgs, vararg fields: K): Boolean? + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not + * exist or the timeout could not be set (see: `EXPIRE`). + * @since 7.0 + */ + suspend fun hexpireat(key: K, timestamp: Long, vararg fields: K): Boolean? + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not + * exist or the timeout could not be set (see: `EXPIRE`). + * @since 7.0 + */ + suspend fun hexpireat(key: K, timestamp: Long, expireArgs: ExpireArgs, vararg fields: K): Boolean? + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not + * exist or the timeout could not be set (see: `EXPIRE`). + * @since 7.0 + */ + suspend fun hexpireat(key: K, timestamp: Date, vararg fields: K): Boolean? + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not + * exist or the timeout could not be set (see: `EXPIRE`). + * @since 7.0 + */ + suspend fun hexpireat(key: K, timestamp: Date, expireArgs: ExpireArgs, vararg fields: K): Boolean? + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not + * exist or the timeout could not be set (see: `EXPIRE`). + * @since 7.0 + */ + suspend fun hexpireat(key: K, timestamp: Instant, vararg fields: K): Boolean? + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: `true` if the timeout was set. `false` if `key` does not + * exist or the timeout could not be set (see: `EXPIRE`). + * @since 7.0 + */ + suspend fun hexpireat(key: K, timestamp: Instant, expireArgs: ExpireArgs, vararg fields: K): Boolean? + + /** + * Get the time to live for one or more fields in as unix timestamp in seconds. + * + * @param key the key. + * @param fields one or more fields to get the TTL for. + * @return Long integer-reply in seconds, or a negative value in order to signal an error. The command returns `-1` if + * the key exists but has no associated expiration time. The command returns `-2` if the key does not exist. + * @since 7.0 + */ + suspend fun hexpiretime(key: K, vararg fields: K): Long? + + /** + * Remove the expiration from one or more fields. + * + * @param key the key. + * @param fields one or more fields to remove the TTL for. + * @return Boolean integer-reply specifically: + * + * `true` if the timeout was removed. `false` if `key` does not exist or does not have an + * associated timeout. + */ + suspend fun hpersist(key: K, vararg fields: K): Boolean? + } diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisHashCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisHashCoroutinesCommandsImpl.kt index ec1a488735..8a1d959e54 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisHashCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisHashCoroutinesCommandsImpl.kt @@ -26,6 +26,9 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.toList import kotlinx.coroutines.reactive.asFlow import kotlinx.coroutines.reactive.awaitFirstOrNull +import java.time.Duration +import java.time.Instant +import java.util.* /** @@ -113,5 +116,60 @@ internal class RedisHashCoroutinesCommandsImpl<K : Any, V : Any>(internal val op override fun hvals(key: K): Flow<V> = ops.hvals(key).asFlow() + override suspend fun hexpire(key: K, seconds: Long, vararg fields: K): Boolean? = + ops.hexpire(key, seconds, *fields).awaitFirstOrNull() + + override suspend fun hexpire(key: K, seconds: Long, expireArgs: ExpireArgs, vararg fields: K): Boolean? = + ops.hexpire(key, seconds, expireArgs, *fields).awaitFirstOrNull() + + override suspend fun hexpire(key: K, seconds: Duration, vararg fields: K): Boolean? = + ops.hexpire(key, seconds, *fields).awaitFirstOrNull() + + override suspend fun hexpire( + key: K, + seconds: Duration, + expireArgs: ExpireArgs, + vararg fields: K + ): Boolean? = + ops.hexpire(key, seconds, expireArgs, *fields).awaitFirstOrNull() + + override suspend fun hexpireat(key: K, timestamp: Date, vararg fields: K): Boolean? = + ops.hexpireat(key, timestamp, *fields).awaitFirstOrNull() + + override suspend fun hexpireat( + key: K, + timestamp: Long, + expireArgs: ExpireArgs, + vararg fields: K + ): Boolean? = + ops.hexpireat(key, timestamp, expireArgs, *fields).awaitFirstOrNull() + + override suspend fun hexpireat(key: K, timestamp: Instant, vararg fields: K): Boolean? = + ops.hexpireat(key, timestamp, *fields).awaitFirstOrNull() + + override suspend fun hexpireat( + key: K, + timestamp: Instant, + expireArgs: ExpireArgs, + vararg fields: K + ): Boolean? = + ops.hexpireat(key, timestamp, expireArgs, *fields).awaitFirstOrNull() + + override suspend fun hexpireat(key: K, timestamp: Long, vararg fields: K): Boolean? = + ops.hexpireat(key, timestamp, *fields).awaitFirstOrNull() + + override suspend fun hexpireat( + key: K, + timestamp: Date, + expireArgs: ExpireArgs, + vararg fields: K + ): Boolean? = + ops.hexpireat(key, timestamp, expireArgs, *fields).awaitFirstOrNull() + + override suspend fun hexpiretime(key: K, vararg fields: K): Long? = + ops.hexpiretime(key).awaitFirstOrNull() + + override suspend fun hpersist(key: K, vararg fields: K): Boolean? = ops.hpersist(key).awaitFirstOrNull() + } diff --git a/src/main/templates/io/lettuce/core/api/RedisHashCommands.java b/src/main/templates/io/lettuce/core/api/RedisHashCommands.java index ddef1ce24f..96dfe525eb 100644 --- a/src/main/templates/io/lettuce/core/api/RedisHashCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisHashCommands.java @@ -428,4 +428,151 @@ public interface RedisHashCommands<K, V> { */ Long hvals(ValueStreamingChannel<V> channel, K key); + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Boolean hexpire(K key, long seconds, K... fields); + + /** + * Set the time to live (in seconds) for one or more fields, belonging to a certain key. + * + * @param key the key of the fields. + * @param seconds the seconds type: long. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Boolean hexpire(K key, long seconds, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL {@link Duration} + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Boolean hexpire(K key, Duration seconds, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key. + * + * @param key the key. + * @param seconds the TTL {@link Duration} + * @param expireArgs the {@link ExpireArgs}. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set. + * @since 7.0 + */ + Boolean hexpire(K key, Duration seconds, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Boolean hexpireat(K key, long timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Boolean hexpireat(K key, long timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Boolean hexpireat(K key, Date timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Boolean hexpireat(K key, Date timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Boolean hexpireat(K key, Instant timestamp, K... fields); + + /** + * Set the time to live for one or more fields, belonging to a certain key as a UNIX timestamp. + * + * @param key the key. + * @param timestamp the timestamp type: posix time. + * @param expireArgs the expire arguments. + * @param fields one or more fields to set the TTL for. + * @return Boolean integer-reply specifically: {@code true} if the timeout was set. {@code false} if {@code key} does not + * exist or the timeout could not be set (see: {@code EXPIRE}). + * @since 7.0 + */ + Boolean hexpireat(K key, Instant timestamp, ExpireArgs expireArgs, K... fields); + + /** + * Get the time to live for one or more fields in as unix timestamp in seconds. + * + * @param key the key. + * @param fields one or more fields to get the TTL for. + * @return Long integer-reply in seconds, or a negative value in order to signal an error. The command returns {@code -1} if + * the key exists but has no associated expiration time. The command returns {@code -2} if the key does not exist. + * @since 7.0 + */ + Long hexpiretime(K key, K... fields); + + /** + * Remove the expiration from one or more fields. + * + * @param key the key. + * @param fields one or more fields to remove the TTL for. + * @return Boolean integer-reply specifically: + * + * {@code true} if the timeout was removed. {@code false} if {@code key} does not exist or does not have an + * associated timeout. + */ + Boolean hpersist(K key, K... fields); } diff --git a/src/test/java/io/lettuce/core/RedisClientIntegrationTests.java b/src/test/java/io/lettuce/core/RedisClientIntegrationTests.java index 2b78d3f99c..57eb8d2bbc 100644 --- a/src/test/java/io/lettuce/core/RedisClientIntegrationTests.java +++ b/src/test/java/io/lettuce/core/RedisClientIntegrationTests.java @@ -1,17 +1,5 @@ package io.lettuce.core; -import static org.assertj.core.api.Assertions.*; - -import java.lang.reflect.Field; -import java.net.SocketAddress; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.Test; - import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.event.command.CommandFailedEvent; import io.lettuce.core.event.command.CommandListener; @@ -27,6 +15,17 @@ import io.lettuce.test.resource.TestClientResources; import io.lettuce.test.settings.TestSettings; import io.netty.util.concurrent.EventExecutorGroup; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.net.SocketAddress; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; /** * Integration tests for {@link RedisClient}. @@ -138,7 +137,9 @@ void shouldPropagateCommandTimeoutToCommandListener() throws InterruptedExceptio assertThat(commandListener.started).hasSize(1); assertThat(commandListener.succeeded).isEmpty(); - assertThat(commandListener.failed).hasSize(1).extracting(it -> it.getCommand().getType()).contains(CommandType.BLPOP); + + Wait.untilTrue(() -> commandListener.failed.size() == 1); + assertThat(commandListener.failed).extracting(it -> it.getCommand().getType()).contains(CommandType.BLPOP); FastShutdown.shutdown(client); } diff --git a/src/test/java/io/lettuce/core/RedisCommandBuilderUnitTests.java b/src/test/java/io/lettuce/core/RedisCommandBuilderUnitTests.java index 9d2b676aae..01b5be2241 100644 --- a/src/test/java/io/lettuce/core/RedisCommandBuilderUnitTests.java +++ b/src/test/java/io/lettuce/core/RedisCommandBuilderUnitTests.java @@ -1,14 +1,14 @@ package io.lettuce.core; -import static org.assertj.core.api.Assertions.*; - -import java.nio.charset.StandardCharsets; - -import org.junit.jupiter.api.Test; - import io.lettuce.core.codec.StringCodec; import io.lettuce.core.protocol.Command; +import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import org.junit.jupiter.api.Test; + +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; /** * Unit tests for {@link RedisCommandBuilder}. @@ -16,6 +16,11 @@ * @author Mark Paluch */ class RedisCommandBuilderUnitTests { + public static final String MY_KEY = "hKey"; + public static final String MY_FIELD1 = "hField1"; + public static final String MY_FIELD2 = "hField2"; + public static final String MY_FIELD3 = "hField3"; + RedisCommandBuilder<String, String> sut = new RedisCommandBuilder<>(StringCodec.UTF8); @@ -29,4 +34,53 @@ void shouldCorrectlyConstructXreadgroup() { .isEqualTo("stream"); } + @Test + void shouldCorrectlyConstructHexpire() { + + Command<String, String, ?> command = + sut.hexpire(MY_KEY, 1, ExpireArgs.Builder.nx(), MY_FIELD1, MY_FIELD2, MY_FIELD3); + ByteBuf buf = Unpooled.directBuffer(); + command.encode(buf); + + assertThat(buf.toString(StandardCharsets.UTF_8)).isEqualTo("*8\r\n" + "$7\r\n" + "HEXPIRE\r\n" + "$4\r\n" + + "hKey\r\n" + "$1\r\n" + "1\r\n" + "$2\r\n" + "NX\r\n" + "$1\r\n" + "3\r\n" + "$7\r\n" + "hField1\r\n" + + "$7\r\n" + "hField2\r\n" + "$7\r\n" + "hField3\r\n"); + } + + @Test + void shouldCorrectlyConstructHexpireat() { + + Command<String, String, ?> command = + sut.hexpireat(MY_KEY, 1, ExpireArgs.Builder.nx(), MY_FIELD1, MY_FIELD2, MY_FIELD3); + ByteBuf buf = Unpooled.directBuffer(); + command.encode(buf); + + assertThat(buf.toString(StandardCharsets.UTF_8)).isEqualTo("*8\r\n" + "$9\r\n" + "HEXPIREAT\r\n" + "$4\r\n" + + "hKey\r\n" + "$1\r\n" + "1\r\n" + "$2\r\n" + "NX\r\n" + "$1\r\n" + "3\r\n" + "$7\r\n" + "hField1\r\n" + + "$7\r\n" + "hField2\r\n" + "$7\r\n" + "hField3\r\n"); + } + + @Test + void shouldCorrectlyConstructHexpiretime() { + + Command<String, String, ?> command = sut.hexpiretime(MY_KEY, MY_FIELD1, MY_FIELD2, MY_FIELD3); + ByteBuf buf = Unpooled.directBuffer(); + command.encode(buf); + + assertThat(buf.toString(StandardCharsets.UTF_8)).isEqualTo("*6\r\n" + "$11\r\n" + "HEXPIRETIME\r\n" + "$4\r\n" + + "hKey\r\n" + "$1\r\n" + "3\r\n" + "$7\r\n" + "hField1\r\n" + "$7\r\n" + "hField2\r\n" + "$7\r\n" + + "hField3\r\n"); + } + + @Test + void shouldCorrectlyConstructHpersist() { + + Command<String, String, ?> command = sut.hpersist(MY_KEY, MY_FIELD1, MY_FIELD2, MY_FIELD3); + ByteBuf buf = Unpooled.directBuffer(); + command.encode(buf); + + assertThat(buf.toString(StandardCharsets.UTF_8)).isEqualTo("*6\r\n" + "$8\r\n" + "HPERSIST\r\n" + "$4\r\n" + + "hKey\r\n" + "$1\r\n" + "3\r\n" + "$7\r\n" + "hField1\r\n" + "$7\r\n" + "hField2\r\n" + "$7\r\n" + + "hField3\r\n"); + } } diff --git a/src/test/java/io/lettuce/core/commands/HashCommandIntegrationTests.java b/src/test/java/io/lettuce/core/commands/HashCommandIntegrationTests.java index e346b29128..b87102505d 100644 --- a/src/test/java/io/lettuce/core/commands/HashCommandIntegrationTests.java +++ b/src/test/java/io/lettuce/core/commands/HashCommandIntegrationTests.java @@ -19,24 +19,9 @@ */ package io.lettuce.core.commands; -import static org.assertj.core.api.Assertions.*; - -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.inject.Inject; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.extension.ExtendWith; - -import io.lettuce.core.KeyValue; +import io.lettuce.core.ExpireArgs; import io.lettuce.core.KeyScanCursor; +import io.lettuce.core.KeyValue; import io.lettuce.core.MapScanCursor; import io.lettuce.core.ScanArgs; import io.lettuce.core.ScanCursor; @@ -47,6 +32,26 @@ import io.lettuce.test.LettuceExtension; import io.lettuce.test.ListStreamingAdapter; import io.lettuce.test.condition.EnabledOnCommand; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.ExtendWith; + +import javax.inject.Inject; +import java.time.Duration; +import java.time.Instant; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.offset; +import static org.awaitility.Awaitility.await; /** * Integration tests for {@link io.lettuce.core.api.sync.RedisHashCommands}. @@ -58,6 +63,9 @@ @ExtendWith(LettuceExtension.class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class HashCommandIntegrationTests extends TestSupport { + public static final String MY_KEY = "hKey"; + public static final String MY_FIELD = "hField"; + public static final String MY_VALUE = "hValue"; private final RedisCommands<String, String> redis; @@ -71,6 +79,13 @@ void setUp() { this.redis.flushall(); } + @AfterEach + void tearDown() { + // resets the configuration settings to default, would not be needed once listpack is supported + assertThat(redis.configSet("hash-max-listpack-entries","512")).isEqualTo("OK"); + assertThat(redis.configSet("set-max-listpack-value","64")).isEqualTo("OK"); + } + @Test void hdel() { assertThat(redis.hdel(key, "one")).isEqualTo(0); @@ -541,6 +556,81 @@ void hscanNoValuesMatch() { assertThat(cursor.getKeys()).hasSize(11); } + @Test + @EnabledOnCommand("HEXPIRE") + void hexpire() { + // the below settings are required until the solution is able to support listpack entries + // see TODOs in https://github.com/redis/redis/pull/13172 for more details + assertThat(redis.configSet("hash-max-listpack-entries","0")).isEqualTo("OK"); + assertThat(redis.configSet("set-max-listpack-value","0")).isEqualTo("OK"); + + assertThat(redis.hset(MY_KEY, MY_FIELD, MY_VALUE)).isTrue(); + assertThat(redis.hexpire(MY_KEY, 1, MY_FIELD)).isTrue(); + + await().until(() -> redis.hget(MY_KEY, MY_FIELD) == null); + } + + @Test + @EnabledOnCommand("HEXPIRE") + void hexpireExpireArgs() { + // the below settings are required until the solution is able to support listpack entries + // see TODOs in https://github.com/redis/redis/pull/13172 for more details + assertThat(redis.configSet("hash-max-listpack-entries","0")).isEqualTo("OK"); + assertThat(redis.configSet("set-max-listpack-value","0")).isEqualTo("OK"); + + assertThat(redis.hset(MY_KEY, MY_FIELD, MY_VALUE)).isTrue(); + assertThat(redis.hexpire(MY_KEY, Duration.ofSeconds(1), ExpireArgs.Builder.nx(), MY_FIELD)).isTrue(); + assertThat(redis.hexpire(MY_KEY, Duration.ofSeconds(1), ExpireArgs.Builder.xx(), MY_FIELD)).isTrue(); + assertThat(redis.hexpire(MY_KEY, Duration.ofSeconds(10), ExpireArgs.Builder.gt(), MY_FIELD)).isTrue(); + assertThat(redis.hexpire(MY_KEY, Duration.ofSeconds(1), ExpireArgs.Builder.lt(), MY_FIELD)).isTrue(); + + await().until(() -> redis.hget(MY_KEY, MY_FIELD) == null); + } + + @Test + @EnabledOnCommand("HEXPIREAT") + void hexpireat() { + // the below settings are required until the solution is able to support listpack entries + // see TODOs in https://github.com/redis/redis/pull/13172 for more details + assertThat(redis.configSet("hash-max-listpack-entries","0")).isEqualTo("OK"); + assertThat(redis.configSet("set-max-listpack-value","0")).isEqualTo("OK"); + + assertThat(redis.hset(MY_KEY, MY_FIELD, MY_VALUE)).isTrue(); + assertThat(redis.hexpireat(MY_KEY,Instant.now().plusSeconds(1) , MY_FIELD)).isTrue(); + + await().until(() -> redis.hget(MY_KEY, MY_FIELD) == null); + } + + @Test + @EnabledOnCommand("HEXPIRETIME") + void hexpiretime() { + Date expiration = new Date(System.currentTimeMillis() + 10000); + // the below settings are required until the solution is able to support listpack entries + // see TODOs in https://github.com/redis/redis/pull/13172 for more details + assertThat(redis.configSet("hash-max-listpack-entries","0")).isEqualTo("OK"); + assertThat(redis.configSet("set-max-listpack-value","0")).isEqualTo("OK"); + + assertThat(redis.hset(MY_KEY, MY_FIELD, MY_VALUE)).isTrue(); + assertThat(redis.hexpireat(MY_KEY, expiration, MY_FIELD)).isTrue(); + + assertThat(redis.hexpiretime(MY_KEY, MY_FIELD)).isEqualTo(expiration.getTime() / 1000); + } + + @Test + @EnabledOnCommand("HPERSIST") + void persist() { + // the below settings are required until the solution is able to support listpack entries + // see TODOs in https://github.com/redis/redis/pull/13172 for more details + assertThat(redis.configSet("hash-max-listpack-entries","0")).isEqualTo("OK"); + assertThat(redis.configSet("set-max-listpack-value","0")).isEqualTo("OK"); + + assertThat(redis.hpersist(MY_KEY, MY_FIELD)).isFalse(); + assertThat(redis.hset(MY_KEY, MY_FIELD, MY_VALUE)).isTrue(); + assertThat(redis.hpersist(MY_KEY, MY_FIELD)).isFalse(); + assertThat(redis.hexpire(MY_KEY, 1, MY_FIELD)).isTrue(); + assertThat(redis.hpersist(MY_KEY, MY_FIELD)).isTrue(); + } + void setup100KeyValues(Map<String, String> expect) { for (int i = 0; i < 100; i++) { expect.put(key + i, value + 1);