DATAREDIS-316 - Add EX/PX, NX/XX options to SET command.

We now support EX/PX and NX/XX arguments along with the SET command for both xetorthio/jedis and mp911de/lettuce.

Jedis 2.8 does not support all combinations of EX/PX and NX/XX. To work around those limitations we delegate to the according set methods to execute related commands or fail fast if there is no way for delegation.

Original pull request: #170.
This commit is contained in:
Christoph Strobl
2016-02-15 15:25:05 +01:00
committed by Mark Paluch
parent 68da4a4ee5
commit 0c6c127b5e
18 changed files with 1239 additions and 16 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2016 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.
@@ -36,6 +36,7 @@ import org.springframework.data.redis.connection.convert.SetConverter;
import org.springframework.data.redis.core.ConvertingCursor;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@@ -730,6 +731,15 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
delegate.set(key, value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStringCommands#set(byte[], byte[], org.springframework.data.redis.core.types.Expiration, org.springframework.data.redis.connection.RedisStringCommands.SetOptions)
*/
@Override
public void set(byte[] key, byte[] value, Expiration expiration, SetOption option) {
delegate.set(key, value, expiration, option);
}
public Boolean setBit(byte[] key, long offset, boolean value) {
return delegate.setBit(key, offset, value);
}
@@ -1860,6 +1870,14 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
delegate.set(serialize(key), serialize(value));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#set(java.lang.String, java.lang.String, org.springframework.data.redis.core.types.Expiration, org.springframework.data.redis.connection.RedisStringCommands.SetOptions)
*/
public void set(String key, String value, Expiration expiration, SetOption option) {
set(serialize(key), serialize(value), expiration, option);
}
public Boolean setBit(String key, long offset, boolean value) {
return delegate.setBit(serialize(key), offset, value);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2014 the original author or authors.
* Copyright 2011-2016 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.
@@ -18,6 +18,8 @@ package org.springframework.data.redis.connection;
import java.util.List;
import java.util.Map;
import org.springframework.data.redis.core.types.Expiration;
/**
* String/Value-specific commands supported by Redis.
*
@@ -71,6 +73,18 @@ public interface RedisStringCommands {
*/
void set(byte[] key, byte[] value);
/**
* Set {@code value} for {@code key} applying timeouts from {@code expiration} if set and inserting/updating values
* depending on {@code options}.
*
* @param key must not be {@literal null}.
* @param value must not be {@literal null}.
* @param expiration can be {@literal null}. Defaulted to {@link Expiration#persistent()}.
* @param option can be {@literal null}. Defaulted to {@link SetOption#UPSERT}.
* @since 1.7
*/
void set(byte[] key, byte[] value, Expiration expiration, SetOption option);
/**
* Set {@code value} for {@code key}, only if {@code key} does not exist.
* <p>
@@ -278,4 +292,61 @@ public interface RedisStringCommands {
* @return
*/
Long strLen(byte[] key);
/**
* {@code SET} command arguments for {@code NX}, {@code XX}.
*
* @author Christoph Strobl
* @since 1.7
*/
public static enum SetOption {
/**
* Do not set any additional command argument.
*
* @return
*/
UPSERT,
/**
* {@code NX}
*
* @return
*/
SET_IF_ABSENT,
/**
* {@code XX}
*
* @return
*/
SET_IF_PRESENT;
/**
* Do not set any additional command argument.
*
* @return
*/
public static SetOption upsert() {
return UPSERT;
}
/**
* {@code XX}
*
* @return
*/
public static SetOption ifPresent() {
return SET_IF_PRESENT;
}
/**
* {@code NX}
*
* @return
*/
public static SetOption ifAbsent() {
return SET_IF_ABSENT;
}
}
}

View File

@@ -24,6 +24,7 @@ import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.data.redis.serializer.RedisSerializer;
@@ -95,6 +96,18 @@ public interface StringRedisConnection extends RedisConnection {
void set(String key, String value);
/**
* Set {@code value} for {@code key} applying timeouts from {@code expiration} if set and inserting/updating values
* depending on {@code options}.
*
* @param key must not be {@literal null}.
* @param value must not be {@literal null}.
* @param expiration can be {@literal null}. Defaulted to {@link Expiration#persistent()}.
* @param option can be {@literal null}. Defaulted to {@link SetOption#UPSERT}.
* @since 1.7
*/
void set(String key, String value, Expiration expiration, SetOption option);
Boolean setNX(String key, String value);
void setEx(String key, long seconds, String value);

View File

@@ -28,6 +28,7 @@ import java.util.Map.Entry;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.dao.DataAccessException;
@@ -61,10 +62,11 @@ import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanCursor;
import org.springframework.data.redis.core.ScanIteration;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.ObjectUtils;
import redis.clients.jedis.BinaryJedisPubSub;
import redis.clients.jedis.Jedis;
@@ -249,7 +251,8 @@ public class JedisClusterConnection implements RedisClusterConnection {
@Override
public byte[] randomKey() {
List<RedisClusterNode> nodes = new ArrayList<RedisClusterNode>(topologyProvider.getTopology().getActiveMasterNodes());
List<RedisClusterNode> nodes = new ArrayList<RedisClusterNode>(topologyProvider.getTopology()
.getActiveMasterNodes());
Set<RedisNode> inspectedNodes = new HashSet<RedisNode>(nodes.size());
do {
@@ -594,6 +597,49 @@ public class JedisClusterConnection implements RedisClusterConnection {
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStringCommands#set(byte[], byte[], org.springframework.data.redis.core.types.Expiration, org.springframework.data.redis.connection.RedisStringCommands.SetOptions)
*/
@Override
public void set(byte[] key, byte[] value, Expiration expiration, SetOption option) {
if (expiration == null || expiration.isPersitent()) {
if (option == null || ObjectUtils.nullSafeEquals(SetOption.UPSERT, option)) {
set(key, value);
} else {
// BinaryCluster does not support set with nxxx and binary key/value pairs.
if (ObjectUtils.nullSafeEquals(SetOption.SET_IF_PRESENT, option)) {
throw new UnsupportedOperationException("Jedis does not support SET XX without PX or EX on BinaryCluster.");
}
setNX(key, value);
}
} else {
if (option == null || ObjectUtils.nullSafeEquals(SetOption.UPSERT, option)) {
if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) {
pSetEx(key, expiration.getExpirationTime(), value);
} else {
setEx(key, expiration.getExpirationTime(), value);
}
} else {
byte[] nxxx = JedisConverters.toSetCommandNxXxArgument(option);
byte[] expx = JedisConverters.toSetCommandExPxArgument(expiration);
try {
cluster.set(key, value, nxxx, expx, expiration.getExpirationTime());
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStringCommands#setNX(byte[], byte[])
@@ -3432,7 +3478,8 @@ public class JedisClusterConnection implements RedisClusterConnection {
@Override
public void clusterForget(final RedisClusterNode node) {
Set<RedisClusterNode> nodes = new LinkedHashSet<RedisClusterNode>(topologyProvider.getTopology().getActiveMasterNodes());
Set<RedisClusterNode> nodes = new LinkedHashSet<RedisClusterNode>(topologyProvider.getTopology()
.getActiveMasterNodes());
final RedisClusterNode nodeToRemove = topologyProvider.getTopology().lookup(node);
nodes.remove(nodeToRemove);
@@ -3536,8 +3583,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
Assert.notNull(master, "Master cannot be null!");
final RedisClusterNode nodeToUse = topologyProvider.getTopology()
.lookup(master);
final RedisClusterNode nodeToUse = topologyProvider.getTopology().lookup(master);
return JedisConverters.toSetOfRedisClusterNodes(clusterCommandExecutor.executeCommandOnSingleNode(
new JedisClusterCommandCallback<List<String>>() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2016 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.
@@ -54,6 +54,7 @@ import org.springframework.data.redis.core.KeyBoundCursor;
import org.springframework.data.redis.core.ScanCursor;
import org.springframework.data.redis.core.ScanIteration;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -1139,6 +1140,87 @@ public class JedisConnection extends AbstractRedisConnection {
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStringCommands#set(byte[], byte[], org.springframework.data.redis.core.types.Expiration, org.springframework.data.redis.connection.RedisStringCommands.SetOption)
*/
@Override
public void set(byte[] key, byte[] value, Expiration expiration, SetOption option) {
if (expiration == null || expiration.isPersitent()) {
if (option == null || ObjectUtils.nullSafeEquals(SetOption.UPSERT, option)) {
set(key, value);
} else {
try {
byte[] nxxx = JedisConverters.toSetCommandNxXxArgument(option);
if (isPipelined()) {
pipeline(new JedisStatusResult(pipeline.set(key, value, nxxx)));
return;
}
if (isQueueing()) {
transaction(new JedisStatusResult(transaction.set(key, value, nxxx)));
return;
}
jedis.set(key, value, nxxx);
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}
} else {
if (option == null || ObjectUtils.nullSafeEquals(SetOption.UPSERT, option)) {
if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, expiration.getTimeUnit())) {
pSetEx(key, expiration.getExpirationTime(), value);
} else {
setEx(key, expiration.getExpirationTime(), value);
}
} else {
byte[] nxxx = JedisConverters.toSetCommandNxXxArgument(option);
byte[] expx = JedisConverters.toSetCommandExPxArgument(expiration);
try {
if (isPipelined()) {
if (expiration.getExpirationTime() > Integer.MAX_VALUE) {
throw new IllegalArgumentException(
"Expiration.exprirationTime must be less than equals Integer.MAX_VALUE for pipeline in Jedis.");
}
pipeline(new JedisStatusResult(pipeline.set(key, value, nxxx, expx, (int) expiration.getExpirationTime())));
return;
}
if (isQueueing()) {
if (expiration.getExpirationTime() > Integer.MAX_VALUE) {
throw new IllegalArgumentException(
"Expiration.exprirationTime must be less than equals Integer.MAX_VALUE for transactions in Jedis.");
}
transaction(new JedisStatusResult(transaction.set(key, value, nxxx, expx,
(int) expiration.getExpirationTime())));
return;
}
jedis.set(key, value, nxxx, expx, expiration.getExpirationTime());
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}
}
}
public byte[] getSet(byte[] key, byte[] value) {
try {
if (isPipelined()) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2016 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.
@@ -17,11 +17,11 @@ package org.springframework.data.redis.connection.jedis;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.DataAccessException;
@@ -29,7 +29,9 @@ import org.springframework.data.redis.connection.DefaultTuple;
import org.springframework.data.redis.connection.RedisClusterNode;
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.data.redis.connection.RedisServer;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.RedisStringCommands.BitOperation;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
import org.springframework.data.redis.connection.RedisZSetCommands.Range.Boundary;
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
import org.springframework.data.redis.connection.SortParameters;
@@ -40,9 +42,11 @@ import org.springframework.data.redis.connection.convert.ListConverter;
import org.springframework.data.redis.connection.convert.MapConverter;
import org.springframework.data.redis.connection.convert.SetConverter;
import org.springframework.data.redis.connection.convert.StringToRedisClientInfoConverter;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import redis.clients.jedis.BinaryClient.LIST_POSITION;
@@ -71,11 +75,17 @@ abstract public class JedisConverters extends Converters {
private static final Converter<redis.clients.jedis.Tuple, Tuple> TUPLE_CONVERTER;
private static final ListConverter<redis.clients.jedis.Tuple, Tuple> TUPLE_LIST_TO_TUPLE_LIST_CONVERTER;
private static final Converter<Object, RedisClusterNode> OBJECT_TO_CLUSTER_NODE_CONVERTER;
private static final Converter<Expiration, byte[]> EXPIRATION_TO_COMMAND_OPTION_CONVERTER;
private static final Converter<SetOption, byte[]> SET_OPTION_TO_COMMAND_OPTION_CONVERTER;
public static final byte[] PLUS_BYTES;
public static final byte[] MINUS_BYTES;
public static final byte[] POSITIVE_INFINITY_BYTES;
public static final byte[] NEGATIVE_INFINITY_BYTES;
private static final byte[] EX;
private static final byte[] PX;
private static final byte[] NX;
private static final byte[] XX;
static {
@@ -113,6 +123,46 @@ abstract public class JedisConverters extends Converters {
((Number) nodeInfo.get(1)).intValue(), range);
}
};
EX = toBytes("EX");
PX = toBytes("PX");
EXPIRATION_TO_COMMAND_OPTION_CONVERTER = new Converter<Expiration, byte[]>() {
@Override
public byte[] convert(Expiration source) {
if (source == null || source.isPersitent()) {
return new byte[0];
}
if (ObjectUtils.nullSafeEquals(TimeUnit.MILLISECONDS, source.getTimeUnit())) {
return PX;
}
return EX;
}
};
NX = toBytes("NX");
XX = toBytes("XX");
SET_OPTION_TO_COMMAND_OPTION_CONVERTER = new Converter<RedisStringCommands.SetOption, byte[]>() {
@Override
public byte[] convert(SetOption source) {
switch (source) {
case UPSERT:
return new byte[0];
case SET_IF_ABSENT:
return NX;
case SET_IF_PRESENT:
return XX;
}
throw new IllegalArgumentException(String.format("Invalid argument %s for SetOption.", source));
}
};
}
public static Converter<String, byte[]> stringToBytes() {
@@ -322,6 +372,42 @@ abstract public class JedisConverters extends Converters {
return boundaryToBytes(boundary, toBytes("["), toBytes("("));
}
/**
* Converts a given {@link Expiration} to the according {@code SET} command argument.<br />
* <dl>
* <dt>{@link TimeUnit#SECONDS}</dt>
* <dd>{@code EX}</dd>
* <dt>{@link TimeUnit#MILLISECONDS}</dt>
* <dd>{@code PX}</dd>
* </dl>
*
* @param expiration
* @return
* @since 1.7
*/
public static byte[] toSetCommandExPxArgument(Expiration expiration) {
return EXPIRATION_TO_COMMAND_OPTION_CONVERTER.convert(expiration);
}
/**
* Converts a given {@link SetOption} to the according {@code SET} command argument.<br />
* <dl>
* <dt>{@link SetOption#UPSERT}</dt>
* <dd>{@code byte[0]}</dd>
* <dt>{@link SetOption#SET_IF_ABSENT}</dt>
* <dd>{@code NX}</dd>
* <dt>{@link SetOption#SET_IF_PRESENT}</dt>
* <dd>{@code XX}</dd>
* </dl>
*
* @param option
* @return
* @since 1.7
*/
public static byte[] toSetCommandNxXxArgument(SetOption option) {
return SET_OPTION_TO_COMMAND_OPTION_CONVERTER.convert(option);
}
private static byte[] boundaryToBytes(Boundary boundary, byte[] inclPrefix, byte[] exclPrefix) {
byte[] prefix = boundary.isIncluding() ? inclPrefix : exclPrefix;

View File

@@ -47,6 +47,7 @@ import org.springframework.data.redis.connection.SortParameters;
import org.springframework.data.redis.connection.Subscription;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -470,6 +471,16 @@ public class JredisConnection extends AbstractRedisConnection {
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStringCommands#set(byte[], byte[], org.springframework.data.redis.core.types.Expiration, org.springframework.data.redis.connection.RedisStringCommands.SetOption)
*/
@Override
public void set(byte[] key, byte[] value, Expiration expiration, SetOption option) {
throw new UnsupportedOperationException(
"SET with options is not supported for JRedis. Please use SETNX, SETEX, PSETEX.");
}
public byte[] getSet(byte[] key, byte[] value) {
try {
return jredis.getset(key, value);

View File

@@ -62,6 +62,7 @@ import org.springframework.data.redis.core.RedisCommand;
import org.springframework.data.redis.core.ScanCursor;
import org.springframework.data.redis.core.ScanIteration;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -1218,6 +1219,30 @@ public class LettuceConnection extends AbstractRedisConnection {
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStringCommands#set(byte[], byte[], org.springframework.data.redis.core.types.Expiration, org.springframework.data.redis.connection.RedisStringCommands.SetOption)
*/
@Override
public void set(byte[] key, byte[] value, Expiration expiration, SetOption option) {
try {
if (isPipelined()) {
pipeline(new LettuceStatusResult(getAsyncConnection().set(key, value,
LettuceConverters.toSetArgs(expiration, option))));
return;
}
if (isQueueing()) {
transaction(new LettuceTxStatusResult(getConnection().set(key, value,
LettuceConverters.toSetArgs(expiration, option))));
return;
}
getConnection().set(key, value, LettuceConverters.toSetArgs(expiration, option));
} catch (Exception ex) {
throw convertLettuceAccessException(ex);
}
}
public byte[] getSet(byte[] key, byte[] value) {
try {
if (isPipelined()) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2016 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.
@@ -26,6 +26,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.DataAccessException;
@@ -39,6 +40,7 @@ import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisNode.NodeType;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisServer;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
import org.springframework.data.redis.connection.RedisZSetCommands.Range.Boundary;
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
import org.springframework.data.redis.connection.ReturnType;
@@ -47,6 +49,7 @@ import org.springframework.data.redis.connection.SortParameters.Order;
import org.springframework.data.redis.connection.convert.Converters;
import org.springframework.data.redis.connection.convert.LongToBooleanConverter;
import org.springframework.data.redis.connection.convert.StringToRedisClientInfoConverter;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@@ -60,6 +63,7 @@ import com.lambdaworks.redis.SortArgs;
import com.lambdaworks.redis.cluster.models.partitions.Partitions;
import com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode.NodeFlag;
import com.lambdaworks.redis.protocol.LettuceCharsets;
import com.lambdaworks.redis.protocol.SetArgs;
/**
* Lettuce type converters
@@ -614,4 +618,43 @@ abstract public class LettuceConverters extends Converters {
return CLUSTER_NODE_TO_CLUSTER_NODE_CONVERTER.convert(source);
}
/**
* Converts a given {@link Expiration} and {@link SetOption} to the according {@link SetArgs}.<br />
*
* @param expiration can be {@literal null}.
* @param option can be {@literal null}.
* @since 1.7
*/
public static SetArgs toSetArgs(Expiration expiration, SetOption option) {
SetArgs args = new SetArgs();
if (expiration != null && !expiration.isPersitent()) {
switch (expiration.getTimeUnit()) {
case SECONDS:
args.ex(expiration.getExpirationTime());
break;
default:
args.px(expiration.getConverted(TimeUnit.MILLISECONDS));
break;
}
}
if (option != null) {
switch (option) {
case SET_IF_ABSENT:
args.nx();
break;
case SET_IF_PRESENT:
args.xx();
break;
default:
break;
}
}
return args;
}
}

View File

@@ -46,6 +46,7 @@ import org.springframework.data.redis.connection.SortParameters;
import org.springframework.data.redis.connection.Subscription;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.util.Assert;
@@ -875,6 +876,16 @@ public class SrpConnection extends AbstractRedisConnection {
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStringCommands#set(byte[], byte[], org.springframework.data.redis.core.types.Expiration, org.springframework.data.redis.connection.RedisStringCommands.SetOption)
*/
@Override
public void set(byte[] key, byte[] value, Expiration expiration, SetOption option) {
throw new UnsupportedOperationException(
"SET with options is not supported for Srp. Please use SETNX, SETEX, PSETEX.");
}
public byte[] getSet(byte[] key, byte[] value) {
try {
if (isPipelined()) {

View File

@@ -0,0 +1,129 @@
/*
* Copyright 2016 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
*
* http://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.core.types;
import java.util.concurrent.TimeUnit;
import org.springframework.util.Assert;
/**
* Expiration holds a value with its associated {@link TimeUnit}.
*
* @author Christoph Strobl
* @since 1.7
*/
public class Expiration {
private long expirationTime;
private TimeUnit timeUnit;
/**
* Creates new {@link Expiration}.
*
* @param expirationTime can be {@literal null}. Defaulted to {@link TimeUnit#SECONDS}
* @param timeUnit
*/
protected Expiration(long expirationTime, TimeUnit timeUnit) {
this.expirationTime = expirationTime;
this.timeUnit = timeUnit != null ? timeUnit : TimeUnit.SECONDS;
}
/**
* Get the expiration time converted into {@link TimeUnit#MILLISECONDS}.
*
* @return
*/
public long getExpirationTimeInMilliseconds() {
return getConverted(TimeUnit.MILLISECONDS);
}
/**
* Get the expiration time converted into {@link TimeUnit#SECONDS}.
*
* @return
*/
public long getExpirationTimeInSeconds() {
return getConverted(TimeUnit.SECONDS);
}
/**
* Get the expiration time.
*
* @return
*/
public long getExpirationTime() {
return expirationTime;
}
/**
* Get the time unit for the expiration time.
*
* @return
*/
public TimeUnit getTimeUnit() {
return this.timeUnit;
}
/**
* Get the expiration time converted into the desired {@code targetTimeUnit}.
*
* @param targetTimeUnit must not {@literal null}.
* @return
* @throws IllegalArgumentException
*/
public long getConverted(TimeUnit targetTimeUnit) {
Assert.notNull(targetTimeUnit, "TargetTimeUnit must not be null!");
return targetTimeUnit.convert(expirationTime, timeUnit);
}
/**
* Creates new {@link Expiration} with {@link TimeUnit#SECONDS}.
*
* @param expirationTime
* @return
*/
public static Expiration seconds(long expirationTime) {
return new Expiration(expirationTime, TimeUnit.SECONDS);
}
/**
* Creates new {@link Expiration} with {@link TimeUnit#MILLISECONDS}.
*
* @param expirationTime
* @return
*/
public static Expiration milliseconds(long expirationTime) {
return new Expiration(expirationTime, TimeUnit.MILLISECONDS);
}
/**
* Creates new persistent {@link Expiration}.
*
* @return
*/
public static Expiration persistent() {
return new Expiration(-1, TimeUnit.SECONDS);
}
/**
* @return {@literal true} if {@link Expiration} is set to persistent.
*/
public boolean isPersitent() {
return expirationTime == -1;
}
}