@@ -17,6 +17,7 @@ package org.springframework.data.redis.connection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.redis.core.types.Expiration;
|
||||
@@ -58,10 +59,13 @@ public interface RedisStringCommands {
|
||||
|
||||
/**
|
||||
* Return the value at {@code key} and expire the key by applying {@link Expiration}.
|
||||
* <p />
|
||||
* Use {@link Expiration#seconds(long)} for {@code EX}. <br />
|
||||
* Use {@link Expiration#milliseconds(long)} for {@code PX}. <br />
|
||||
* Use {@link Expiration#unixTimestamp(long, TimeUnit)} for {@code EXAT | PXAT}. <br />
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param expiration must not be {@literal null}.
|
||||
* @param unit must not be {@literal null}.
|
||||
* @return {@literal null} when key does not exist or used in pipeline / transaction.
|
||||
* @see <a href="https://redis.io/commands/getex">Redis Documentation: GETEX</a>
|
||||
* @since 2.6
|
||||
|
||||
@@ -443,12 +443,13 @@ public abstract class JedisConverters extends Converters {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a given {@link Expiration} to the according {@code GETEX} command argument.
|
||||
* Converts a given {@link Expiration} to the according {@code GETEX} command argument depending on
|
||||
* {@link Expiration#isUnixTimestamp()}.
|
||||
* <dl>
|
||||
* <dt>{@link TimeUnit#MILLISECONDS}</dt>
|
||||
* <dd>{@code PX}</dd>
|
||||
* <dd>{@code PX|PXAT}</dd>
|
||||
* <dt>{@link TimeUnit#SECONDS}</dt>
|
||||
* <dd>{@code EX}</dd>
|
||||
* <dd>{@code EX|EXAT}</dd>
|
||||
* </dl>
|
||||
*
|
||||
* @param expiration must not be {@literal null}.
|
||||
@@ -464,10 +465,14 @@ public abstract class JedisConverters extends Converters {
|
||||
}
|
||||
|
||||
if (expiration.getTimeUnit() == TimeUnit.MILLISECONDS) {
|
||||
if (expiration.isUnixTimestamp()) {
|
||||
return params.pxAt(expiration.getExpirationTime());
|
||||
}
|
||||
return params.px(expiration.getExpirationTime());
|
||||
}
|
||||
|
||||
return params.ex((int) expiration.getExpirationTime());
|
||||
return expiration.isUnixTimestamp() ? params.exAt(expiration.getConverted(TimeUnit.SECONDS))
|
||||
: params.ex(expiration.getConverted(TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -728,32 +728,31 @@ public abstract class LettuceConverters extends Converters {
|
||||
/**
|
||||
* Convert {@link Expiration} to {@link GetExArgs}.
|
||||
*
|
||||
* @param expiration
|
||||
* @param expiration can be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
*/
|
||||
static GetExArgs toGetExArgs(Expiration expiration) {
|
||||
static GetExArgs toGetExArgs(@Nullable Expiration expiration) {
|
||||
|
||||
GetExArgs args = new GetExArgs();
|
||||
|
||||
if (expiration != null) {
|
||||
|
||||
if (expiration.isPersistent()) {
|
||||
args.persist();
|
||||
} else if (!expiration.isPersistent()) {
|
||||
|
||||
switch (expiration.getTimeUnit()) {
|
||||
case SECONDS:
|
||||
args.ex(expiration.getExpirationTime());
|
||||
break;
|
||||
default:
|
||||
args.px(expiration.getConverted(TimeUnit.MILLISECONDS));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (expiration == null) {
|
||||
return args;
|
||||
}
|
||||
|
||||
return args;
|
||||
if(expiration.isPersistent()) {
|
||||
return args.persist();
|
||||
}
|
||||
|
||||
if (expiration.getTimeUnit() == TimeUnit.MILLISECONDS) {
|
||||
if (expiration.isUnixTimestamp()) {
|
||||
return args.pxAt(expiration.getExpirationTime());
|
||||
}
|
||||
return args.px(expiration.getExpirationTime());
|
||||
}
|
||||
|
||||
return expiration.isUnixTimestamp() ? args.exAt(expiration.getConverted(TimeUnit.SECONDS))
|
||||
: args.ex(expiration.getConverted(TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
static Converter<List<byte[]>, Long> toTimeConverter(TimeUnit timeUnit) {
|
||||
|
||||
@@ -115,6 +115,17 @@ public class Expiration {
|
||||
return new Expiration(expirationTime, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link Expiration} with the given {@literal unix timestamp} and {@link TimeUnit}.
|
||||
*
|
||||
* @param unixTimestamp the unix timestamp at which the key will expire.
|
||||
* @param timeUnit must not be {@literal null}.
|
||||
* @return new instance of {@link Expiration}.
|
||||
*/
|
||||
public static Expiration unixTimestamp(long unixTimestamp, TimeUnit timeUnit) {
|
||||
return new ExpireAt(unixTimestamp, timeUnit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain an {@link Expiration} that indicates to keep the existing one. Eg. when sending a {@code SET} command.
|
||||
* <p />
|
||||
@@ -197,6 +208,14 @@ public class Expiration {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if {@link Expiration} is set to a specified Unix time at which the key will expire.
|
||||
* @since 2.6
|
||||
*/
|
||||
public boolean isUnixTimestamp() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.4
|
||||
@@ -214,4 +233,19 @@ public class Expiration {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.6
|
||||
*/
|
||||
private static class ExpireAt extends Expiration {
|
||||
|
||||
private ExpireAt(long expirationTime, @Nullable TimeUnit timeUnit) {
|
||||
super(expirationTime, timeUnit);
|
||||
}
|
||||
|
||||
public boolean isUnixTimestamp() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1138,6 +1138,7 @@ public abstract class AbstractConnectionIntegrationTests {
|
||||
@Test // GH-2050
|
||||
@EnabledOnCommand("GETEX")
|
||||
void testGetEx() {
|
||||
|
||||
actual.add(connection.set("testGS", "1"));
|
||||
actual.add(connection.getEx("testGS", Expiration.seconds(10)));
|
||||
actual.add(connection.ttl("testGS"));
|
||||
@@ -1150,6 +1151,7 @@ public abstract class AbstractConnectionIntegrationTests {
|
||||
@Test // GH-2050
|
||||
@EnabledOnCommand("GETDEL")
|
||||
void testGetDel() {
|
||||
|
||||
actual.add(connection.set("testGS", "1"));
|
||||
actual.add(connection.getDel("testGS"));
|
||||
actual.add(connection.exists("testGS"));
|
||||
@@ -1159,6 +1161,7 @@ public abstract class AbstractConnectionIntegrationTests {
|
||||
|
||||
@Test
|
||||
void testGetSet() {
|
||||
|
||||
actual.add(connection.set("testGS", "1"));
|
||||
actual.add(connection.getSet("testGS", "2"));
|
||||
actual.add(connection.get("testGS"));
|
||||
@@ -1167,6 +1170,7 @@ public abstract class AbstractConnectionIntegrationTests {
|
||||
|
||||
@Test
|
||||
void testMSet() {
|
||||
|
||||
Map<String, String> vals = new HashMap<>();
|
||||
vals.put("color", "orange");
|
||||
vals.put("size", "1");
|
||||
@@ -1178,6 +1182,7 @@ public abstract class AbstractConnectionIntegrationTests {
|
||||
|
||||
@Test
|
||||
void testMSetNx() {
|
||||
|
||||
Map<String, String> vals = new HashMap<>();
|
||||
vals.put("height", "5");
|
||||
vals.put("width", "1");
|
||||
@@ -1188,6 +1193,7 @@ public abstract class AbstractConnectionIntegrationTests {
|
||||
|
||||
@Test
|
||||
void testMSetNxFailure() {
|
||||
|
||||
actual.add(connection.set("height", "2"));
|
||||
Map<String, String> vals = new HashMap<>();
|
||||
vals.put("height", "5");
|
||||
@@ -1199,6 +1205,7 @@ public abstract class AbstractConnectionIntegrationTests {
|
||||
|
||||
@Test
|
||||
void testSetNx() {
|
||||
|
||||
actual.add(connection.setNX("notaround", "54"));
|
||||
actual.add(connection.get("notaround"));
|
||||
actual.add(connection.setNX("notaround", "55"));
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.redis.connection.jedis;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import redis.clients.jedis.params.GetExParams;
|
||||
import redis.clients.jedis.params.SetParams;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -26,6 +27,7 @@ import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisServer;
|
||||
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
|
||||
@@ -226,6 +228,54 @@ class JedisConvertersUnitTests {
|
||||
assertThat(toString(JedisConverters.toSetCommandNxXxArgument(SetOption.upsert()))).isEqualTo("");
|
||||
}
|
||||
|
||||
@Test // GH-2050
|
||||
void convertsExpirationToGetExEX() {
|
||||
|
||||
assertThat(JedisConverters.toGetExParams(Expiration.seconds(10)))
|
||||
.extracting(GetExParams::toString)
|
||||
.isEqualTo(new GetExParams().ex(10).toString());
|
||||
}
|
||||
|
||||
@Test // GH-2050
|
||||
void convertsExpirationWithTimeUnitToGetExEX() {
|
||||
|
||||
assertThat(JedisConverters.toGetExParams(Expiration.from(1, TimeUnit.MINUTES)))
|
||||
.extracting(GetExParams::toString)
|
||||
.isEqualTo(new GetExParams().ex(60).toString());
|
||||
}
|
||||
|
||||
@Test // GH-2050
|
||||
void convertsExpirationToGetExPEX() {
|
||||
|
||||
assertThat(JedisConverters.toGetExParams(Expiration.milliseconds(10)))
|
||||
.extracting(GetExParams::toString)
|
||||
.isEqualTo(new GetExParams().px(10).toString());
|
||||
}
|
||||
|
||||
@Test // GH-2050
|
||||
void convertsExpirationToGetExEXAT() {
|
||||
|
||||
assertThat(JedisConverters.toGetExParams(Expiration.unixTimestamp(10, TimeUnit.SECONDS)))
|
||||
.extracting(GetExParams::toString)
|
||||
.isEqualTo(new GetExParams().exAt(10).toString());
|
||||
}
|
||||
|
||||
@Test // GH-2050
|
||||
void convertsExpirationWithTimeUnitToGetExEXAT() {
|
||||
|
||||
assertThat(JedisConverters.toGetExParams(Expiration.unixTimestamp(1, TimeUnit.MINUTES)))
|
||||
.extracting(GetExParams::toString)
|
||||
.isEqualTo(new GetExParams().exAt(60).toString());
|
||||
}
|
||||
|
||||
@Test // GH-2050
|
||||
void convertsExpirationToGetExPXAT() {
|
||||
|
||||
assertThat(JedisConverters.toGetExParams(Expiration.unixTimestamp(10, TimeUnit.MILLISECONDS)))
|
||||
.extracting(GetExParams::toString)
|
||||
.isEqualTo(new GetExParams().pxAt(10).toString());
|
||||
}
|
||||
|
||||
private void verifyRedisServerInfo(RedisServer server, Map<String, String> values) {
|
||||
|
||||
for (Map.Entry<String, String> entry : values.entrySet()) {
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import io.lettuce.core.CompositeArgument;
|
||||
import io.lettuce.core.codec.StringCodec;
|
||||
import io.lettuce.core.protocol.CommandArgs;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class LettuceCommandArgsComparator {
|
||||
|
||||
static void argsEqual(CompositeArgument args1, CompositeArgument args2) {
|
||||
|
||||
CommandArgs<String, String> stringStringCommandArgs1 = new CommandArgs<>(StringCodec.UTF8);
|
||||
args1.build(stringStringCommandArgs1);
|
||||
|
||||
CommandArgs<String, String> stringStringCommandArgs2 = new CommandArgs<>(StringCodec.UTF8);
|
||||
args2.build(stringStringCommandArgs2);
|
||||
|
||||
Assertions.assertThat(stringStringCommandArgs1.toCommandString())
|
||||
.isEqualTo(stringStringCommandArgs2.toCommandString());
|
||||
}
|
||||
|
||||
static LettuceCompositeArgumentAssert assertThatCommandArgument(CompositeArgument command) {
|
||||
return new LettuceCompositeArgumentAssert() {
|
||||
|
||||
@Override
|
||||
public LettuceCompositeArgumentAssert isEqualTo(CompositeArgument expected) {
|
||||
|
||||
argsEqual(command, expected);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
interface LettuceCompositeArgumentAssert {
|
||||
LettuceCompositeArgumentAssert isEqualTo(CompositeArgument command);
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,10 @@ package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.redis.connection.ClusterTestVariables.*;
|
||||
import static org.springframework.data.redis.connection.lettuce.LettuceCommandArgsComparator.*;
|
||||
import static org.springframework.test.util.ReflectionTestUtils.*;
|
||||
|
||||
import io.lettuce.core.GetExArgs;
|
||||
import io.lettuce.core.Limit;
|
||||
import io.lettuce.core.RedisURI;
|
||||
import io.lettuce.core.SetArgs;
|
||||
@@ -29,9 +31,9 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisClusterNode;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode.Flag;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode.LinkState;
|
||||
@@ -193,4 +195,45 @@ class LettuceConvertersUnitTests {
|
||||
assertThat(limit.isLimited()).isTrue();
|
||||
assertThat(limit.getCount()).isEqualTo(5L);
|
||||
}
|
||||
|
||||
@Test // GH-2050
|
||||
void convertsExpirationToGetExEX() {
|
||||
|
||||
assertThatCommandArgument(LettuceConverters.toGetExArgs(Expiration.seconds(10))).isEqualTo(new GetExArgs().ex(10));
|
||||
}
|
||||
|
||||
@Test // GH-2050
|
||||
void convertsExpirationWithTimeUnitToGetExEX() {
|
||||
|
||||
assertThatCommandArgument(LettuceConverters.toGetExArgs(Expiration.from(1, TimeUnit.MINUTES)))
|
||||
.isEqualTo(new GetExArgs().ex(60));
|
||||
}
|
||||
|
||||
@Test // GH-2050
|
||||
void convertsExpirationToGetExPEX() {
|
||||
|
||||
assertThatCommandArgument(LettuceConverters.toGetExArgs(Expiration.milliseconds(10)))
|
||||
.isEqualTo(new GetExArgs().px(10));
|
||||
}
|
||||
|
||||
@Test // GH-2050
|
||||
void convertsExpirationToGetExEXAT() {
|
||||
|
||||
assertThatCommandArgument(LettuceConverters.toGetExArgs(Expiration.unixTimestamp(10, TimeUnit.SECONDS)))
|
||||
.isEqualTo(new GetExArgs().exAt(10));
|
||||
}
|
||||
|
||||
@Test // GH-2050
|
||||
void convertsExpirationWithTimeUnitToGetExEXAT() {
|
||||
|
||||
assertThatCommandArgument(LettuceConverters.toGetExArgs(Expiration.unixTimestamp(1, TimeUnit.MINUTES)))
|
||||
.isEqualTo(new GetExArgs().exAt(60));
|
||||
}
|
||||
|
||||
@Test // GH-2050
|
||||
void convertsExpirationToGetExPXAT() {
|
||||
|
||||
assertThatCommandArgument(LettuceConverters.toGetExArgs(Expiration.unixTimestamp(10, TimeUnit.MILLISECONDS)))
|
||||
.isEqualTo(new GetExArgs().pxAt(10));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user