Skip to content

Commit f0b22ad

Browse files
Add GETEX options EXAT, PXAT
Original Pull Request: #2086
1 parent a531939 commit f0b22ad

File tree

8 files changed

+219
-21
lines changed

8 files changed

+219
-21
lines changed

src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.List;
1919
import java.util.Map;
20+
import java.util.concurrent.TimeUnit;
2021

2122
import org.springframework.data.domain.Range;
2223
import org.springframework.data.redis.core.types.Expiration;
@@ -58,10 +59,13 @@ enum BitOperation {
5859

5960
/**
6061
* Return the value at {@code key} and expire the key by applying {@link Expiration}.
62+
* <p />
63+
* Use {@link Expiration#seconds(long)} for {@code EX}. <br />
64+
* Use {@link Expiration#milliseconds(long)} for {@code PX}. <br />
65+
* Use {@link Expiration#unixTimestamp(long, TimeUnit)} for {@code EXAT | PXAT}. <br />
6166
*
6267
* @param key must not be {@literal null}.
6368
* @param expiration must not be {@literal null}.
64-
* @param unit must not be {@literal null}.
6569
* @return {@literal null} when key does not exist or used in pipeline / transaction.
6670
* @see <a href="https://redis.io/commands/getex">Redis Documentation: GETEX</a>
6771
* @since 2.6

src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java

+9-4
Original file line numberDiff line numberDiff line change
@@ -443,12 +443,13 @@ public static SetParams toSetCommandExPxArgument(Expiration expiration, SetParam
443443
}
444444

445445
/**
446-
* Converts a given {@link Expiration} to the according {@code GETEX} command argument.
446+
* Converts a given {@link Expiration} to the according {@code GETEX} command argument depending on
447+
* {@link Expiration#isUnixTimestamp()}.
447448
* <dl>
448449
* <dt>{@link TimeUnit#MILLISECONDS}</dt>
449-
* <dd>{@code PX}</dd>
450+
* <dd>{@code PX|PXAT}</dd>
450451
* <dt>{@link TimeUnit#SECONDS}</dt>
451-
* <dd>{@code EX}</dd>
452+
* <dd>{@code EX|EXAT}</dd>
452453
* </dl>
453454
*
454455
* @param expiration must not be {@literal null}.
@@ -464,10 +465,14 @@ static GetExParams toGetExParams(Expiration expiration) {
464465
}
465466

466467
if (expiration.getTimeUnit() == TimeUnit.MILLISECONDS) {
468+
if (expiration.isUnixTimestamp()) {
469+
return params.pxAt(expiration.getExpirationTime());
470+
}
467471
return params.px(expiration.getExpirationTime());
468472
}
469473

470-
return params.ex((int) expiration.getExpirationTime());
474+
return expiration.isUnixTimestamp() ? params.exAt(expiration.getConverted(TimeUnit.SECONDS))
475+
: params.ex(expiration.getConverted(TimeUnit.SECONDS));
471476
}
472477

473478
/**

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java

+14-15
Original file line numberDiff line numberDiff line change
@@ -728,32 +728,31 @@ public static SetArgs toSetArgs(@Nullable Expiration expiration, @Nullable SetOp
728728
/**
729729
* Convert {@link Expiration} to {@link GetExArgs}.
730730
*
731-
* @param expiration
731+
* @param expiration can be {@literal null}.
732732
* @return
733733
* @since 2.6
734734
*/
735-
static GetExArgs toGetExArgs(Expiration expiration) {
735+
static GetExArgs toGetExArgs(@Nullable Expiration expiration) {
736736

737737
GetExArgs args = new GetExArgs();
738738

739-
if (expiration != null) {
739+
if (expiration == null) {
740+
return args;
741+
}
740742

741-
if (expiration.isPersistent()) {
742-
args.persist();
743-
} else if (!expiration.isPersistent()) {
743+
if(expiration.isPersistent()) {
744+
return args.persist();
745+
}
744746

745-
switch (expiration.getTimeUnit()) {
746-
case SECONDS:
747-
args.ex(expiration.getExpirationTime());
748-
break;
749-
default:
750-
args.px(expiration.getConverted(TimeUnit.MILLISECONDS));
751-
break;
752-
}
747+
if (expiration.getTimeUnit() == TimeUnit.MILLISECONDS) {
748+
if (expiration.isUnixTimestamp()) {
749+
return args.pxAt(expiration.getExpirationTime());
753750
}
751+
return args.px(expiration.getExpirationTime());
754752
}
755753

756-
return args;
754+
return expiration.isUnixTimestamp() ? args.exAt(expiration.getConverted(TimeUnit.SECONDS))
755+
: args.ex(expiration.getConverted(TimeUnit.SECONDS));
757756
}
758757

759758
static Converter<List<byte[]>, Long> toTimeConverter(TimeUnit timeUnit) {

src/main/java/org/springframework/data/redis/core/types/Expiration.java

+34
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,17 @@ public static Expiration milliseconds(long expirationTime) {
115115
return new Expiration(expirationTime, TimeUnit.MILLISECONDS);
116116
}
117117

118+
/**
119+
* Creates new {@link Expiration} with the given {@literal unix timestamp} and {@link TimeUnit}.
120+
*
121+
* @param unixTimestamp the unix timestamp at which the key will expire.
122+
* @param timeUnit must not be {@literal null}.
123+
* @return new instance of {@link Expiration}.
124+
*/
125+
public static Expiration unixTimestamp(long unixTimestamp, TimeUnit timeUnit) {
126+
return new ExpireAt(unixTimestamp, timeUnit);
127+
}
128+
118129
/**
119130
* Obtain an {@link Expiration} that indicates to keep the existing one. Eg. when sending a {@code SET} command.
120131
* <p />
@@ -197,6 +208,14 @@ public boolean isKeepTtl() {
197208
return false;
198209
}
199210

211+
/**
212+
* @return {@literal true} if {@link Expiration} is set to a specified Unix time at which the key will expire.
213+
* @since 2.6
214+
*/
215+
public boolean isUnixTimestamp() {
216+
return false;
217+
}
218+
200219
/**
201220
* @author Christoph Strobl
202221
* @since 2.4
@@ -214,4 +233,19 @@ public boolean isKeepTtl() {
214233
return true;
215234
}
216235
}
236+
237+
/**
238+
* @author Christoph Strobl
239+
* @since 2.6
240+
*/
241+
private static class ExpireAt extends Expiration {
242+
243+
private ExpireAt(long expirationTime, @Nullable TimeUnit timeUnit) {
244+
super(expirationTime, timeUnit);
245+
}
246+
247+
public boolean isUnixTimestamp() {
248+
return true;
249+
}
250+
}
217251
}

src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java

+7
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,7 @@ void testType() {
11381138
@Test // GH-2050
11391139
@EnabledOnCommand("GETEX")
11401140
void testGetEx() {
1141+
11411142
actual.add(connection.set("testGS", "1"));
11421143
actual.add(connection.getEx("testGS", Expiration.seconds(10)));
11431144
actual.add(connection.ttl("testGS"));
@@ -1150,6 +1151,7 @@ void testGetEx() {
11501151
@Test // GH-2050
11511152
@EnabledOnCommand("GETDEL")
11521153
void testGetDel() {
1154+
11531155
actual.add(connection.set("testGS", "1"));
11541156
actual.add(connection.getDel("testGS"));
11551157
actual.add(connection.exists("testGS"));
@@ -1159,6 +1161,7 @@ void testGetDel() {
11591161

11601162
@Test
11611163
void testGetSet() {
1164+
11621165
actual.add(connection.set("testGS", "1"));
11631166
actual.add(connection.getSet("testGS", "2"));
11641167
actual.add(connection.get("testGS"));
@@ -1167,6 +1170,7 @@ void testGetSet() {
11671170

11681171
@Test
11691172
void testMSet() {
1173+
11701174
Map<String, String> vals = new HashMap<>();
11711175
vals.put("color", "orange");
11721176
vals.put("size", "1");
@@ -1178,6 +1182,7 @@ void testMSet() {
11781182

11791183
@Test
11801184
void testMSetNx() {
1185+
11811186
Map<String, String> vals = new HashMap<>();
11821187
vals.put("height", "5");
11831188
vals.put("width", "1");
@@ -1188,6 +1193,7 @@ void testMSetNx() {
11881193

11891194
@Test
11901195
void testMSetNxFailure() {
1196+
11911197
actual.add(connection.set("height", "2"));
11921198
Map<String, String> vals = new HashMap<>();
11931199
vals.put("height", "5");
@@ -1199,6 +1205,7 @@ void testMSetNxFailure() {
11991205

12001206
@Test
12011207
void testSetNx() {
1208+
12021209
actual.add(connection.setNX("notaround", "54"));
12031210
actual.add(connection.get("notaround"));
12041211
actual.add(connection.setNX("notaround", "55"));

src/test/java/org/springframework/data/redis/connection/jedis/JedisConvertersUnitTests.java

+50
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.assertj.core.api.Assertions.*;
1919

2020
import org.junit.jupiter.api.Test;
21+
import redis.clients.jedis.params.GetExParams;
2122
import redis.clients.jedis.params.SetParams;
2223

2324
import java.util.Arrays;
@@ -26,6 +27,7 @@
2627
import java.util.HashMap;
2728
import java.util.List;
2829
import java.util.Map;
30+
import java.util.concurrent.TimeUnit;
2931

3032
import org.springframework.data.redis.connection.RedisServer;
3133
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
@@ -226,6 +228,54 @@ void toSetCommandNxXxOptionShouldReturnEmptyArrayforUpsert() {
226228
assertThat(toString(JedisConverters.toSetCommandNxXxArgument(SetOption.upsert()))).isEqualTo("");
227229
}
228230

231+
@Test // GH-2050
232+
void convertsExpirationToGetExEX() {
233+
234+
assertThat(JedisConverters.toGetExParams(Expiration.seconds(10)))
235+
.extracting(GetExParams::toString)
236+
.isEqualTo(new GetExParams().ex(10).toString());
237+
}
238+
239+
@Test // GH-2050
240+
void convertsExpirationWithTimeUnitToGetExEX() {
241+
242+
assertThat(JedisConverters.toGetExParams(Expiration.from(1, TimeUnit.MINUTES)))
243+
.extracting(GetExParams::toString)
244+
.isEqualTo(new GetExParams().ex(60).toString());
245+
}
246+
247+
@Test // GH-2050
248+
void convertsExpirationToGetExPEX() {
249+
250+
assertThat(JedisConverters.toGetExParams(Expiration.milliseconds(10)))
251+
.extracting(GetExParams::toString)
252+
.isEqualTo(new GetExParams().px(10).toString());
253+
}
254+
255+
@Test // GH-2050
256+
void convertsExpirationToGetExEXAT() {
257+
258+
assertThat(JedisConverters.toGetExParams(Expiration.unixTimestamp(10, TimeUnit.SECONDS)))
259+
.extracting(GetExParams::toString)
260+
.isEqualTo(new GetExParams().exAt(10).toString());
261+
}
262+
263+
@Test // GH-2050
264+
void convertsExpirationWithTimeUnitToGetExEXAT() {
265+
266+
assertThat(JedisConverters.toGetExParams(Expiration.unixTimestamp(1, TimeUnit.MINUTES)))
267+
.extracting(GetExParams::toString)
268+
.isEqualTo(new GetExParams().exAt(60).toString());
269+
}
270+
271+
@Test // GH-2050
272+
void convertsExpirationToGetExPXAT() {
273+
274+
assertThat(JedisConverters.toGetExParams(Expiration.unixTimestamp(10, TimeUnit.MILLISECONDS)))
275+
.extracting(GetExParams::toString)
276+
.isEqualTo(new GetExParams().pxAt(10).toString());
277+
}
278+
229279
private void verifyRedisServerInfo(RedisServer server, Map<String, String> values) {
230280

231281
for (Map.Entry<String, String> entry : values.entrySet()) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.redis.connection.lettuce;
17+
18+
import io.lettuce.core.CompositeArgument;
19+
import io.lettuce.core.codec.StringCodec;
20+
import io.lettuce.core.protocol.CommandArgs;
21+
22+
import org.assertj.core.api.Assertions;
23+
24+
/**
25+
* @author Christoph Strobl
26+
*/
27+
public class LettuceCommandArgsComparator {
28+
29+
static void argsEqual(CompositeArgument args1, CompositeArgument args2) {
30+
31+
CommandArgs<String, String> stringStringCommandArgs1 = new CommandArgs<>(StringCodec.UTF8);
32+
args1.build(stringStringCommandArgs1);
33+
34+
CommandArgs<String, String> stringStringCommandArgs2 = new CommandArgs<>(StringCodec.UTF8);
35+
args2.build(stringStringCommandArgs2);
36+
37+
Assertions.assertThat(stringStringCommandArgs1.toCommandString())
38+
.isEqualTo(stringStringCommandArgs2.toCommandString());
39+
}
40+
41+
static LettuceCompositeArgumentAssert assertThatCommandArgument(CompositeArgument command) {
42+
return new LettuceCompositeArgumentAssert() {
43+
44+
@Override
45+
public LettuceCompositeArgumentAssert isEqualTo(CompositeArgument expected) {
46+
47+
argsEqual(command, expected);
48+
return this;
49+
}
50+
};
51+
}
52+
53+
interface LettuceCompositeArgumentAssert {
54+
LettuceCompositeArgumentAssert isEqualTo(CompositeArgument command);
55+
}
56+
}

0 commit comments

Comments
 (0)