Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>3.4.0-SNAPSHOT</version>
<version>3.4.0-GH-2853-SNAPSHOT</version>

<name>Spring Data Redis</name>
<description>Spring Data module for Redis</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,11 @@ public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption op
return convertAndReturn(delegate.set(key, value, expiration, option), Converters.identityConverter());
}

@Override
public byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) {
return convertAndReturn(delegate.setGet(key, value, expiration, option), Converters.identityConverter());
}

@Override
public Boolean setBit(byte[] key, long offset, boolean value) {
return convertAndReturn(delegate.setBit(key, offset, value), Converters.identityConverter());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,13 @@ default Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption o
return stringCommands().set(key, value, expiration, option);
}

/** @deprecated in favor of {@link RedisConnection#stringCommands()}}. */
@Override
@Deprecated
default byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) {
return stringCommands().setGet(key, value, expiration, option);
}

/** @deprecated in favor of {@link RedisConnection#stringCommands()}}. */
@Override
@Deprecated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Marcin Grzejszczak
* @since 2.0
*/
public interface ReactiveStringCommands {
Expand All @@ -62,15 +63,23 @@ class SetCommand extends KeyCommand {
private @Nullable ByteBuffer value;
private Expiration expiration;
private SetOption option;
private boolean get;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That isn't going to work as the set API returns a boolean response.


private SetCommand(ByteBuffer key, @Nullable ByteBuffer value, @Nullable Expiration expiration,
@Nullable SetOption option) {

this(key, value, expiration, option, false);
}

private SetCommand(ByteBuffer key, @Nullable ByteBuffer value, @Nullable Expiration expiration,
@Nullable SetOption option, boolean get) {

super(key);

this.value = value;
this.expiration = expiration;
this.option = option;
this.get = get;
}

/**
Expand Down Expand Up @@ -125,6 +134,16 @@ public SetCommand withSetOption(SetOption option) {
return new SetCommand(getKey(), value, expiration, option);
}

/**
* Applies GET option. Return the old string stored at key, or {@code null} if key did not exist.
* An error is returned and SET aborted if the value stored at key is not a string.
*
* @return a new {@link SetCommand} with {@link SetOption} applied.
*/
public SetCommand withGet() {
return new SetCommand(getKey(), value, expiration, option, true);
}

/**
* @return
*/
Expand All @@ -146,6 +165,13 @@ public Optional<Expiration> getExpiration() {
public Optional<SetOption> getOption() {
return Optional.ofNullable(option);
}

/**
* @return
*/
public boolean isGet() {
return get;
}
}

/**
Expand Down Expand Up @@ -184,6 +210,22 @@ default Mono<Boolean> set(ByteBuffer key, ByteBuffer value, Expiration expiratio
.map(BooleanResponse::getOutput);
}

/**
* Set {@literal value} for {@literal key} with {@literal expiration} and {@literal options}. Return the old
* string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value
* stored at key is not a string.
*
* @param key must not be {@literal null}.
* @param value must not be {@literal null}.
* @param expiration must not be {@literal null}. Use {@link Expiration#persistent()} for no expiration time or
* {@link Expiration#keepTtl()} to keep the existing.
* @param option must not be {@literal null}.
* @return
* @see <a href="https://redis.io/commands/set">Redis Documentation: SET</a>
* @since 3.4
*/
Flux<ByteBufferResponse<SetCommand>> setGet(ByteBuffer key, ByteBuffer value, Expiration expiration, SetOption option);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we need both signatures, Mono<ByteBuffer> setGet(ByteBuffer key, ByteBuffer value, Expiration expiration, SetOption option) { and Flux<ByteBufferResponse<SetCommand>> set(Publisher<SetCommand> commands);

The first one delegates to the latter.


/**
* Set each and every item separately by invoking {@link SetCommand}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,22 @@ enum BitOperation {
@Nullable
Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption option);

/**
* Set {@code value} for {@code key}. Return the old string stored at key, or nil if key did not exist.
* An error is returned and SET aborted if the value stored at key is not a string.
*
* @param key must not be {@literal null}.
* @param value must not be {@literal null}.
* @param expiration must not be {@literal null}. Use {@link Expiration#persistent()} to not set any ttl or
* {@link Expiration#keepTtl()} to keep the existing expiration.
* @param option must not be {@literal null}. Use {@link SetOption#upsert()} to add non existing.
* @return {@literal null} when used in pipeline / transaction.
* @since 3.4
* @see <a href="https://redis.io/commands/set">Redis Documentation: SET</a>
*/
@Nullable
byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option);

/**
* Set {@code value} for {@code key}, only if {@code key} does not exist.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,24 @@ public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption op
}
}

@Override
public byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) {

Assert.notNull(key, "Key must not be null");
Assert.notNull(value, "Value must not be null");
Assert.notNull(expiration, "Expiration must not be null");
Assert.notNull(option, "Option must not be null");

SetParams setParams = JedisConverters.toSetCommandExPxArgument(expiration,
JedisConverters.toSetCommandNxXxArgument(option));

try {
return connection.getCluster().setGet(key, value, setParams);
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}

@Override
public Boolean setNX(byte[] key, byte[] value) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,20 @@ public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption op
.getOrElse(Converters.stringToBooleanConverter(), () -> false);
}

@Override
@Nullable
public byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) {
Assert.notNull(key, "Key must not be null");
Assert.notNull(value, "Value must not be null");
Assert.notNull(expiration, "Expiration must not be null");
Assert.notNull(option, "Option must not be null");

SetParams params = JedisConverters.toSetCommandExPxArgument(expiration,
JedisConverters.toSetCommandNxXxArgument(option));

return connection.invoke().just(Jedis::setGet, PipelineBinaryCommands::setGet, key, value, params);
}

@Override
public Boolean setNX(byte[] key, byte[] value) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,20 @@ public Flux<BooleanResponse<SetCommand>> set(Publisher<SetCommand> commands) {
}));
}

@Override
public Flux<ByteBufferResponse<SetCommand>> setGet(ByteBuffer key, ByteBuffer value, Expiration exp,
RedisStringCommands.SetOption option) {
return this.connection.execute(reactiveCommands -> Flux.from(Mono.just(SetCommand.set(key).value(value).withGet().expiring(exp).withSetOption(option))).concatMap((command) -> {

Assert.notNull(command.getKey(), "Key must not be null");
Assert.notNull(command.getValue(), "Value must not be null");

return reactiveCommands.setGet(command.getKey(), command.getValue())
.map(v -> new ByteBufferResponse<>(command, v))
.defaultIfEmpty(new AbsentByteBufferResponse<>(command));
}));
}

@Override
public Flux<ByteBufferResponse<SetCommand>> getSet(Publisher<SetCommand> commands) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,18 @@ public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption op
.orElse(LettuceConverters.stringToBooleanConverter(), false);
}

@Override
@Nullable
public byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) {
Assert.notNull(key, "Key must not be null");
Assert.notNull(value, "Value must not be null");
Assert.notNull(expiration, "Expiration must not be null");
Assert.notNull(option, "Option must not be null");

return connection.invoke()
.just(RedisStringAsyncCommands::setGet, key, value, LettuceConverters.toSetArgs(expiration, option));
}

@Override
public Boolean setNX(byte[] key, byte[] value) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,27 @@ private boolean failsafeInvokePsetEx(RedisConnection connection) {
});
}

@Override
public V setGet(K key, V value, long timeout, TimeUnit unit) {
return doSetGet(key, value, Expiration.from(timeout, unit));
}

@Override
public V setGet(K key, V value, Duration duration) {
return doSetGet(key, value, Expiration.from(duration));
}

private V doSetGet(K key, V value, Expiration duration) {
byte[] rawValue = rawValue(value);
return execute( new ValueDeserializingRedisCallback(key) {

@Override
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
return connection.stringCommands().setGet(rawKey, rawValue, duration, SetOption.UPSERT);
}
});
}

@Override
public Boolean setIfAbsent(K key, V value) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
* @author Christoph Strobl
* @author Mark Paluch
* @author Jiahe Cai
* @author Marcin Grzejszczak
*/
public interface ValueOperations<K, V> {

Expand All @@ -44,6 +45,33 @@ public interface ValueOperations<K, V> {
*/
void set(K key, V value);

/**
* Set the {@code value} and expiration {@code timeout} for {@code key}. Return the old
* string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value
* stored at key is not a string.
*
* @param key must not be {@literal null}.
* @param value must not be {@literal null}.
* @param timeout the key expiration timeout.
* @param unit must not be {@literal null}.
* @see <a href="https://redis.io/commands/set">Redis Documentation: SET</a>
* @since 3.4
*/
V setGet(K key, V value, long timeout, TimeUnit unit);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to update BoundValueOperations (the interface) and ReactiveValueOperations as well.


/**
* Set the {@code value} and expiration {@code timeout} for {@code key}. Return the old
* string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value
* stored at key is not a string.
*
* @param key must not be {@literal null}.
* @param value must not be {@literal null}.
* @param duration expiration duration
* @see <a href="https://redis.io/commands/set">Redis Documentation: SET</a>
* @since 3.4
*/
V setGet(K key, V value, Duration duration);

/**
* Set the {@code value} and expiration {@code timeout} for {@code key}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1093,6 +1093,11 @@ public Boolean set(byte[] key, byte[] value, Expiration expiration, SetOption op
return delegate.set(key, value, expiration, options);
}

@Override
public byte[] setGet(byte[] key, byte[] value, Expiration expiration, SetOption option) {
return delegate.setGet(key, value, expiration, option);
}

@Override
public List<Long> bitField(byte[] key, BitFieldSubCommands subCommands) {
return delegate.bitField(key, subCommands);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -556,4 +556,17 @@ void setKeepTTL() {
assertThat(nativeBinaryCommands.ttl(KEY_1_BBUFFER)).isCloseTo(expireSeconds, Offset.offset(5L));
assertThat(nativeCommands.get(KEY_1)).isEqualTo(VALUE_2);
}

@ParameterizedRedisTest // GH-2853
void setWithGetOption() {
nativeCommands.set(KEY_1, VALUE_1);

connection.stringCommands().setGet(KEY_1_BBUFFER, VALUE_2_BBUFFER, Expiration.keepTtl(), SetOption.upsert())
.map(CommandResponse::getOutput)
.as(StepVerifier::create) //
.expectNext(VALUE_1_BBUFFER) //
.verifyComplete();

assertThat(nativeCommands.get(KEY_1)).isEqualTo(VALUE_2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,32 @@ void testSetWithExpirationWithTimeUnitMilliseconds() {
await().atMost(Duration.ofMillis(500L)).until(() -> !redisTemplate.hasKey(key));
}

@ParameterizedRedisTest
void testSetGetWithExpiration() {

K key = keyFactory.instance();
V value1 = valueFactory.instance();
V value2 = valueFactory.instance();

valueOps.set(key, value1);

assertThat(valueOps.setGet(key, value2, 1, TimeUnit.SECONDS)).isEqualTo(value1);
assertThat(valueOps.get(key)).isEqualTo(value2);
}

@ParameterizedRedisTest
void testSetGetWithExpirationDuration() {

K key = keyFactory.instance();
V value1 = valueFactory.instance();
V value2 = valueFactory.instance();

valueOps.set(key, value1);

assertThat(valueOps.setGet(key, value2, Duration.ofMillis(1000))).isEqualTo(value1);
assertThat(valueOps.get(key)).isEqualTo(value2);
}

@ParameterizedRedisTest
void testAppend() {

Expand Down