DATAREDIS-271 - Add support for 'pSetEx'.

Support for 'pSetEx' has been added for 'jedis' & 'srp'.
For 'lettuce' the command is emulated using 'eval'.

There is no support for 'pSetEx' when using 'jredis'.

'DefaultValueOperations.set' used with 'TimeUnit.MILLISECONDS' will fallback to 'setEx' in case 'pSetEx' is not supported by the driver in use.

Original pull request: #46.
This commit is contained in:
Christoph Strobl
2014-03-13 15:21:45 +01:00
committed by Thomas Darimont
parent a3ac8d0899
commit 4b2ccbecb9
18 changed files with 244 additions and 11 deletions

View File

@@ -735,6 +735,15 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
delegate.setEx(key, seconds, value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStringCommands#pSetEx(byte[], long, byte[])
*/
@Override
public void pSetEx(byte[] key, long milliseconds, byte[] value) {
delegate.pSetEx(key, milliseconds, value);
}
public Boolean setNX(byte[] key, byte[] value) {
Boolean result = delegate.setNX(key, value);
if (isFutureConversion()) {
@@ -1695,6 +1704,15 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
delegate.setEx(serialize(key), seconds, serialize(value));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#pSetEx(java.lang.String, long, java.lang.String)
*/
@Override
public void pSetEx(String key, long seconds, String value) {
pSetEx(serialize(key), seconds, serialize(value));
}
public Boolean setNX(String key, String value) {
Boolean result = delegate.setNX(serialize(key), serialize(value));
if (isFutureConversion()) {

View File

@@ -87,6 +87,17 @@ public interface RedisStringCommands {
*/
void setEx(byte[] key, long seconds, byte[] value);
/**
* Set the {@code value} and expiration in {@code milliseconds} for {@code key}.
*
* @see http://redis.io/commands/psetex
* @param key
* @param milliseconds
* @param value
* @since 1.3
*/
void pSetEx(byte[] key, long milliseconds, byte[] value);
/**
* Set multiple keys to multiple values using key-value pairs provided in {@code tuple}.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2014 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.
@@ -29,6 +29,7 @@ import org.springframework.data.redis.serializer.RedisSerializer;
* Uses a {@link RedisSerializer} underneath to perform the conversion.
*
* @author Costin Leau
* @author Christoph Strobl
* @see RedisCallback
* @see RedisSerializer
* @see StringRedisTemplate
@@ -93,6 +94,17 @@ public interface StringRedisConnection extends RedisConnection {
void setEx(String key, long seconds, String value);
/**
* Set the {@code value} and expiration in {@code milliseconds} for {@code key}.
*
* @see http://redis.io/commands/psetex
* @param key
* @param seconds
* @param value
* @since 1.3
*/
void pSetEx(String key, long milliseconds, String value);
void mSetString(Map<String, String> tuple);
Boolean mSetNXString(Map<String, String> tuple);

View File

@@ -331,6 +331,10 @@ public class JedisConnection implements RedisConnection {
return results;
}
private void doPipelined(Response<?> response) {
pipeline(new JedisStatusResult(response));
}
private void pipeline(FutureResult<Response<?>> result) {
if (isQueueing()) {
transaction(result);
@@ -339,6 +343,10 @@ public class JedisConnection implements RedisConnection {
}
}
private void doQueued(Response<?> response) {
transaction(new JedisStatusResult(response));
}
private void transaction(FutureResult<Response<?>> result) {
txResults.add(result);
}
@@ -1141,6 +1149,28 @@ public class JedisConnection implements RedisConnection {
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStringCommands#pSetEx(byte[], long, byte[])
*/
@Override
public void pSetEx(byte[] key, long milliseconds, byte[] value) {
try {
if (isPipelined()) {
doPipelined(pipeline.psetex(key, (int) milliseconds, value));
return;
}
if (isQueueing()) {
doQueued(transaction.psetex(key, (int) milliseconds, value));
return;
}
jedis.psetex(key, (int) milliseconds, value);
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}
public Boolean setNX(byte[] key, byte[] value) {
try {
if (isPipelined()) {

View File

@@ -495,6 +495,15 @@ public class JredisConnection implements RedisConnection {
throw new UnsupportedOperationException();
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStringCommands#pSetEx(byte[], long, byte[])
*/
@Override
public void pSetEx(byte[] key, long milliseconds, byte[] value) {
throw new UnsupportedOperationException();
}
public Boolean setNX(byte[] key, byte[] value) {
try {
return jredis.setnx(key, value);

View File

@@ -56,6 +56,7 @@ import org.springframework.util.ObjectUtils;
import com.lambdaworks.redis.RedisAsyncConnection;
import com.lambdaworks.redis.RedisClient;
import com.lambdaworks.redis.RedisException;
import com.lambdaworks.redis.ScriptOutputType;
import com.lambdaworks.redis.SortArgs;
import com.lambdaworks.redis.ZStoreArgs;
import com.lambdaworks.redis.codec.RedisCodec;
@@ -1197,6 +1198,49 @@ public class LettuceConnection implements RedisConnection {
}
}
/**
* {@code pSetEx} is not directly supported and therefore emulated via {@literal lua script}.
*
* @since 1.3
* @see org.springframework.data.redis.connection.RedisStringCommands#pSetEx(byte[], long, byte[])
*/
@Override
public void pSetEx(byte[] key, long milliseconds, byte[] value) {
byte[] script = createRedisScriptForPSetEx(key, milliseconds, value);
byte[][] emptyArgs = new byte[0][0];
try {
if (isPipelined()) {
pipeline(new LettuceStatusResult(getAsyncConnection().eval(script, ScriptOutputType.STATUS, emptyArgs,
emptyArgs)));
return;
}
if (isQueueing()) {
transaction(new LettuceTxStatusResult(getConnection().eval(script, ScriptOutputType.STATUS, emptyArgs,
emptyArgs)));
return;
}
this.eval(script, ReturnType.STATUS, 0);
} catch (Exception ex) {
throw convertLettuceAccessException(ex);
}
}
private byte[] createRedisScriptForPSetEx(byte[] key, long milliseconds, byte[] value) {
StringBuilder sb = new StringBuilder("return redis.call('PSETEX'");
sb.append(",'");
sb.append(new String(key));
sb.append("',");
sb.append(milliseconds);
sb.append(",'");
sb.append(new String(value));
sb.append("')");
return sb.toString().getBytes();
}
public Boolean setNX(byte[] key, byte[] value) {
try {
if (isPipelined()) {

View File

@@ -900,6 +900,24 @@ public class SrpConnection implements RedisConnection {
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisStringCommands#pSetEx(byte[], long, byte[])
*/
@Override
public void pSetEx(byte[] key, long milliseconds, byte[] value) {
try {
if (isPipelined()) {
doPipelined(pipeline.psetex(key, milliseconds, value));
return;
}
client.psetex(key, milliseconds, value);
} catch (Exception ex) {
throw convertSrpAccessException(ex);
}
}
public Boolean setNX(byte[] key, byte[] value) {
try {
if (isPipelined()) {
@@ -2113,6 +2131,11 @@ public class SrpConnection implements RedisConnection {
}
}
@SuppressWarnings("rawtypes")
private void doPipelined(ListenableFuture<Reply> listenableFuture) {
pipeline(new SrpStatusResult(listenableFuture));
}
// processing method that adds a listener to the future in order to track down the results and close the pipeline
private void pipeline(FutureResult<?> future) {
if (isQueueing()) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2014 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.
@@ -30,6 +30,7 @@ import org.springframework.data.redis.connection.RedisConnection;
*
* @author Costin Leau
* @author Jennifer Hickey
* @author Christoph Strobl
*/
class DefaultValueOperations<K, V> extends AbstractOperations<K, V> implements ValueOperations<K, V> {
@@ -173,17 +174,37 @@ class DefaultValueOperations<K, V> extends AbstractOperations<K, V> implements V
}, true);
}
public void set(K key, V value, long timeout, TimeUnit unit) {
public void set(K key, V value, final long timeout, final TimeUnit unit) {
final byte[] rawKey = rawKey(key);
final byte[] rawValue = rawValue(value);
final long rawTimeout = TimeoutUtils.toSeconds(timeout, unit);
execute(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
connection.setEx(rawKey, rawTimeout, rawValue);
potentiallyUsePsetEx(connection);
return null;
}
public void potentiallyUsePsetEx(RedisConnection connection) {
if (!TimeUnit.MILLISECONDS.equals(unit) || !failsafeInvokePsetEx(connection)) {
connection.setEx(rawKey, TimeoutUtils.toSeconds(timeout, unit), rawValue);
}
}
private boolean failsafeInvokePsetEx(RedisConnection connection) {
boolean failed = false;
try {
connection.pSetEx(rawKey, timeout, rawValue);
} catch (UnsupportedOperationException e) {
// in case the connection does not support pSetEx return false to allow fallback to other operation.
failed = true;
}
return !failed;
}
}, true);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2014 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.
@@ -24,11 +24,21 @@ import java.util.concurrent.TimeUnit;
* Redis operations for simple (or in Redis terminology 'string') values.
*
* @author Costin Leau
* @author Christoph Strobl
*/
public interface ValueOperations<K, V> {
void set(K key, V value);
/**
* Set {@code key} to hold the string {@code value} until {@code timeout}.
*
* @param key
* @param value
* @param timeout
* @param units
* @see http://redis.io/commands/set
*/
void set(K key, V value, long timeout, TimeUnit unit);
Boolean setIfAbsent(K key, V value);