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:
committed by
Mark Paluch
parent
68da4a4ee5
commit
0c6c127b5e
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>>() {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user