Support precise sub second timeout for BZPOPMIN, BZPOPMAX.
In cases where the timeout is defined in msec we calculate a precise timeout for the BZPOP* operations. #2102 will bring this behaviour also to List commands. Original Pull Request: #2088
This commit is contained in:
@@ -25,10 +25,10 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
|
||||
@@ -163,8 +163,7 @@ public interface ReactiveZSetCommands {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies {@literal GT} mode. Constructs a new command
|
||||
* instance with all previously configured properties.
|
||||
* Applies {@literal GT} mode. Constructs a new command instance with all previously configured properties.
|
||||
*
|
||||
* @return a new {@link ZAddCommand} with {@literal incr} applied.
|
||||
* @since 2.5
|
||||
@@ -174,8 +173,7 @@ public interface ReactiveZSetCommands {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies {@literal LT} mode. Constructs a new command
|
||||
* instance with all previously configured properties.
|
||||
* Applies {@literal LT} mode. Constructs a new command instance with all previously configured properties.
|
||||
*
|
||||
* @return a new {@link ZAddCommand} with {@literal incr} applied.
|
||||
* @since 2.5
|
||||
@@ -206,7 +204,6 @@ public interface ReactiveZSetCommands {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {@literal true} if {@literal GT} is set.
|
||||
* @since 2.5
|
||||
*/
|
||||
@@ -1337,15 +1334,18 @@ public interface ReactiveZSetCommands {
|
||||
|
||||
private final PopDirection direction;
|
||||
|
||||
private final Duration timeout;
|
||||
private final @Nullable TimeUnit timeUnit;
|
||||
private final @Nullable Long timeout;
|
||||
|
||||
private final long count;
|
||||
|
||||
private BZPopCommand(@Nullable ByteBuffer key, Duration timeout, long count, PopDirection direction) {
|
||||
private BZPopCommand(@Nullable ByteBuffer key, @Nullable Long timeout, @Nullable TimeUnit timeUnit, long count,
|
||||
PopDirection direction) {
|
||||
|
||||
super(key);
|
||||
this.count = count;
|
||||
this.timeout = timeout;
|
||||
this.timeUnit = timeUnit;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
@@ -1355,7 +1355,7 @@ public interface ReactiveZSetCommands {
|
||||
* @return a new {@link BZPopCommand} for min pop ({@literal ZPOPMIN}).
|
||||
*/
|
||||
public static BZPopCommand min() {
|
||||
return new BZPopCommand(null, null, 0, PopDirection.MIN);
|
||||
return new BZPopCommand(null, null, null, 0, PopDirection.MIN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1364,7 +1364,7 @@ public interface ReactiveZSetCommands {
|
||||
* @return a new {@link BZPopCommand} for max pop ({@literal ZPOPMAX}).
|
||||
*/
|
||||
public static BZPopCommand max() {
|
||||
return new BZPopCommand(null, null, 0, PopDirection.MAX);
|
||||
return new BZPopCommand(null, null, null, 0, PopDirection.MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1377,7 +1377,7 @@ public interface ReactiveZSetCommands {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return new BZPopCommand(key, timeout, count, direction);
|
||||
return new BZPopCommand(key, timeout, timeUnit, count, direction);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1387,7 +1387,7 @@ public interface ReactiveZSetCommands {
|
||||
* @return a new {@link BZPopCommand} with {@literal value} applied.
|
||||
*/
|
||||
public BZPopCommand count(long count) {
|
||||
return new BZPopCommand(getKey(), timeout, count, direction);
|
||||
return new BZPopCommand(getKey(), timeout, timeUnit, count, direction);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1400,7 +1400,21 @@ public interface ReactiveZSetCommands {
|
||||
|
||||
Assert.notNull(timeout, "Timeout must not be null!");
|
||||
|
||||
return new BZPopCommand(getKey(), timeout, count, direction);
|
||||
return blockingFor(timeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a {@link Duration timeout}. Constructs a new command instance with all previously configured properties.
|
||||
*
|
||||
* @param timeout value.
|
||||
* @param timeout must not be {@literal null}.
|
||||
* @return a new {@link BZPopCommand} with {@link Duration timeout} applied.
|
||||
*/
|
||||
public BZPopCommand blockingFor(long timeout, TimeUnit timeUnit) {
|
||||
|
||||
Assert.notNull(timeUnit, "TimeUnit must not be null!");
|
||||
|
||||
return new BZPopCommand(getKey(), timeout, timeUnit, count, direction);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1410,10 +1424,16 @@ public interface ReactiveZSetCommands {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public Duration getTimeout() {
|
||||
@Nullable
|
||||
public Long getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TimeUnit getTimeUnit() {
|
||||
return timeUnit;
|
||||
}
|
||||
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
@@ -1664,7 +1684,7 @@ public interface ReactiveZSetCommands {
|
||||
/**
|
||||
* Creates a new {@link ZMScoreCommand} given a {@link List members}.
|
||||
*
|
||||
* @param member must not be {@literal null}.
|
||||
* @param members must not be {@literal null}.
|
||||
* @return a new {@link ZMScoreCommand} for {@link List} of members.
|
||||
*/
|
||||
public static ZMScoreCommand scoreOf(Collection<ByteBuffer> members) {
|
||||
|
||||
@@ -997,8 +997,8 @@ public interface RedisZSetCommands {
|
||||
Set<Tuple> zPopMin(byte[] key, long count);
|
||||
|
||||
/**
|
||||
* Remove and return the value with its score having the lowest score from sorted set at {@code key}. <b>Blocks
|
||||
* connection</b> until element available or {@code timeout} reached.
|
||||
* Remove and return the value with its score having the lowest score from sorted set at {@code key}. <br />
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param timeout
|
||||
@@ -1034,8 +1034,8 @@ public interface RedisZSetCommands {
|
||||
Set<Tuple> zPopMax(byte[] key, long count);
|
||||
|
||||
/**
|
||||
* Remove and return the value with its score having the highest score from sorted set at {@code key}. <b>Blocks
|
||||
* connection</b> until element available or {@code timeout} reached.
|
||||
* Remove and return the value with its score having the highest score from sorted set at {@code key}. <br />
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param timeout
|
||||
|
||||
@@ -25,8 +25,8 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
@@ -390,7 +390,18 @@ class LettuceReactiveZSetCommands implements ReactiveZSetCommands {
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getTimeout(), "Timeout must not be null!");
|
||||
|
||||
long timeout = command.getTimeout().get(ChronoUnit.SECONDS);
|
||||
if(command.getTimeUnit() == TimeUnit.MILLISECONDS) {
|
||||
|
||||
double timeout = preciseTimeout(command.getTimeout(), command.getTimeUnit());
|
||||
|
||||
Mono<ScoredValue<ByteBuffer>> result = (command.getDirection() == PopDirection.MIN
|
||||
? cmd.bzpopmin(timeout, command.getKey())
|
||||
: cmd.bzpopmax(timeout, command.getKey())).filter(Value::hasValue).map(Value::getValue);
|
||||
|
||||
return new CommandResponse<>(command, result.filter(Value::hasValue).map(this::toTuple).flux());
|
||||
}
|
||||
|
||||
long timeout = command.getTimeUnit().toSeconds(command.getTimeout());
|
||||
|
||||
Mono<ScoredValue<ByteBuffer>> result = (command.getDirection() == PopDirection.MIN
|
||||
? cmd.bzpopmin(timeout, command.getKey())
|
||||
@@ -624,6 +635,10 @@ class LettuceReactiveZSetCommands implements ReactiveZSetCommands {
|
||||
return new DefaultTuple(ByteUtils.getBytes(value), score);
|
||||
}
|
||||
|
||||
static double preciseTimeout(long val, TimeUnit unit) {
|
||||
return (double) unit.toMillis(val) / 1000.0D;
|
||||
}
|
||||
|
||||
protected LettuceReactiveRedisConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
@@ -316,6 +316,13 @@ class LettuceZSetCommands implements RedisZSetCommands {
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(unit, "TimeUnit must not be null!");
|
||||
|
||||
if(TimeUnit.MILLISECONDS == unit) {
|
||||
|
||||
return connection.invoke(connection.getAsyncDedicatedConnection())
|
||||
.from(RedisSortedSetAsyncCommands::bzpopmin, preciseTimeout(timeout, unit), key)
|
||||
.get(it -> it.map(LettuceConverters::toTuple).getValueOrElse(null));
|
||||
}
|
||||
|
||||
return connection.invoke(connection.getAsyncDedicatedConnection())
|
||||
.from(RedisSortedSetAsyncCommands::bzpopmin, unit.toSeconds(timeout), key)
|
||||
.get(it -> it.map(LettuceConverters::toTuple).getValueOrElse(null));
|
||||
@@ -359,6 +366,13 @@ class LettuceZSetCommands implements RedisZSetCommands {
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(unit, "TimeUnit must not be null!");
|
||||
|
||||
if(TimeUnit.MILLISECONDS == unit) {
|
||||
|
||||
return connection.invoke(connection.getAsyncDedicatedConnection())
|
||||
.from(RedisSortedSetAsyncCommands::bzpopmax, preciseTimeout(timeout, unit), key)
|
||||
.get(it -> it.map(LettuceConverters::toTuple).getValueOrElse(null));
|
||||
}
|
||||
|
||||
return connection.invoke(connection.getAsyncDedicatedConnection())
|
||||
.from(RedisSortedSetAsyncCommands::bzpopmax, unit.toSeconds(timeout), key)
|
||||
.get(it -> it.map(LettuceConverters::toTuple).getValueOrElse(null));
|
||||
@@ -703,4 +717,7 @@ class LettuceZSetCommands implements RedisZSetCommands {
|
||||
return target;
|
||||
}
|
||||
|
||||
static double preciseTimeout(long val, TimeUnit unit) {
|
||||
return (double) unit.toMillis(val) / 1000.0D;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,8 +365,8 @@ public interface ZSetOperations<K, V> {
|
||||
Set<TypedTuple<V>> popMin(K key, long count);
|
||||
|
||||
/**
|
||||
* Remove and return the value with its score having the lowest score from sorted set at {@code key}. <b>Blocks
|
||||
* connection</b> until element available or {@code timeout} reached.
|
||||
* Remove and return the value with its score having the lowest score from sorted set at {@code key}. <br />
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param timeout
|
||||
@@ -379,8 +379,8 @@ public interface ZSetOperations<K, V> {
|
||||
TypedTuple<V> popMin(K key, long timeout, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Remove and return the value with its score having the lowest score from sorted set at {@code key}. <b>Blocks
|
||||
* connection</b> until element available or {@code timeout} reached.
|
||||
* Remove and return the value with its score having the lowest score from sorted set at {@code key}. <br />
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param timeout must not be {@literal null}.
|
||||
@@ -422,8 +422,8 @@ public interface ZSetOperations<K, V> {
|
||||
Set<TypedTuple<V>> popMax(K key, long count);
|
||||
|
||||
/**
|
||||
* Remove and return the value with its score having the highest score from sorted set at {@code key}. <b>Blocks
|
||||
* connection</b> until element available or {@code timeout} reached.
|
||||
* Remove and return the value with its score having the highest score from sorted set at {@code key}. <br />
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param timeout
|
||||
@@ -436,8 +436,8 @@ public interface ZSetOperations<K, V> {
|
||||
TypedTuple<V> popMax(K key, long timeout, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Remove and return the value with its score having the highest score from sorted set at {@code key}. <b>Blocks
|
||||
* connection</b> until element available or {@code timeout} reached.
|
||||
* Remove and return the value with its score having the highest score from sorted set at {@code key}. <br />
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param timeout must not be {@literal null}.
|
||||
|
||||
Reference in New Issue
Block a user