DATAREDIS-645 - Introduce absent values in ReactiveStringCommands.

We now return AbsentByteBufferResponse to indicate absence of a Redis key when retrieving the key as top-level element. CommandResponse indicates via isPresent its presence or absence of a response value. Omission of absent values can cause a stream with a different response structure compared to the request. Retaining the stream sequence is required for response to request correlation.

GET and GETSET commands use presence of CommandResponse to filter response emission. Both commands return a single element so absence does not interferes with the request structure.

Previously, we just returned an empty byte-array that caused downstream errors deserializing and emitting null values that are prohibited in Reactive Streams.

Original Pull Request: #249
This commit is contained in:
Mark Paluch
2017-05-11 13:08:22 +02:00
committed by Christoph Strobl
parent 18a6440e98
commit d6ea867e37
5 changed files with 49 additions and 14 deletions

View File

@@ -245,6 +245,14 @@ public interface ReactiveRedisConnection extends Closeable {
private final I input;
private final O output;
/**
* @return {@literal true} if the response is present. An absent {@link CommandResponse} maps to Redis
* {@literal (nil)}.
*/
public boolean isPresent() {
return true;
}
}
/**
@@ -267,6 +275,26 @@ public interface ReactiveRedisConnection extends Closeable {
}
}
/**
* {@link CommandResponse} implementation for {@link ByteBuffer} responses for absent keys.
*/
class AbsentByteBufferResponse<I> extends ByteBufferResponse<I> {
private final static ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.wrap(new byte[0]);
public AbsentByteBufferResponse(I input) {
super(input, EMPTY_BYTE_BUFFER);
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse#isPresent()
*/
@Override
public boolean isPresent() {
return false;
}
}
/**
* {@link CommandResponse} implementation for {@link List} responses.
*/

View File

@@ -199,7 +199,8 @@ public interface ReactiveStringCommands {
Assert.notNull(key, "Key must not be null!");
return get(Mono.just(new KeyCommand(key))).next().map(CommandResponse::getOutput);
return get(Mono.just(new KeyCommand(key))).next().filter(CommandResponse::isPresent)
.map(CommandResponse::getOutput);
}
/**
@@ -225,7 +226,8 @@ public interface ReactiveStringCommands {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
return getSet(Mono.just(SetCommand.set(key).value(value))).next().map(ByteBufferResponse::getOutput);
return getSet(Mono.just(SetCommand.set(key).value(value))).next().filter(CommandResponse::isPresent)
.map(ByteBufferResponse::getOutput);
}
/**

View File

@@ -24,6 +24,7 @@ import java.util.List;
import org.reactivestreams.Publisher;
import org.springframework.data.domain.Range;
import org.springframework.data.redis.connection.ReactiveRedisConnection.AbsentByteBufferResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.ByteBufferResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
@@ -113,7 +114,7 @@ class LettuceReactiveStringCommands implements ReactiveStringCommands {
}
return cmd.getset(command.getKey(), command.getValue()).map((value) -> new ByteBufferResponse<>(command, value))
.defaultIfEmpty(new ByteBufferResponse<>(command, EMPTY_BYTE_BUFFER));
.defaultIfEmpty(new AbsentByteBufferResponse<>(command));
}));
}
@@ -129,7 +130,7 @@ class LettuceReactiveStringCommands implements ReactiveStringCommands {
Assert.notNull(command.getKey(), "Key must not be null!");
return cmd.get(command.getKey()).map((value) -> new ByteBufferResponse<>(command, value))
.defaultIfEmpty(new ByteBufferResponse<>(command, EMPTY_BYTE_BUFFER));
.defaultIfEmpty(new AbsentByteBufferResponse<>(command));
}));
}