DATAREDIS-529 - Add support for EXISTS taking multiple keys.

Original pull request: #281.
This commit is contained in:
Christoph Strobl
2017-10-02 15:18:58 +02:00
committed by Mark Paluch
parent 143c452313
commit c843269804
14 changed files with 263 additions and 19 deletions

View File

@@ -15,17 +15,8 @@
*/
package org.springframework.data.redis.connection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
@@ -303,6 +294,24 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return convertAndReturn(delegate.exists(key), identityConverter);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#exists(String[])
*/
@Override
public Long exists(String... keys) {
return convertAndReturn(delegate.exists(Arrays.asList(serializeMulti(keys))), identityConverter);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#exists(java.util.Collection)
*/
@Override
public Long exists(Collection<byte[]> keys) {
return convertAndReturn(delegate.exists(keys), identityConverter);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#expire(byte[], long)

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.connection;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -54,6 +55,13 @@ public interface DefaultedRedisConnection extends RedisConnection {
return keyCommands().exists(key);
}
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@Override
@Deprecated
default Long exists(Collection<byte[]> keys) {
return keyCommands().exists(keys);
}
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@Override
@Deprecated

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.redis.connection;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -22,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Key-specific commands supported by Redis.
@@ -40,7 +43,23 @@ public interface RedisKeyCommands {
* @see <a href="http://redis.io/commands/exists">Redis Documentation: EXISTS</a>
*/
@Nullable
Boolean exists(byte[] key);
default Boolean exists(byte[] key) {
Assert.notNull(key, "Key must not be null!");
Long count = exists(Collections.singleton(key));
return count != null ? count > 0 : null;
}
/**
* Count how many of the given {@code keys} exists. Providing the very same {@code key} more than once also counts
* multiple times.
*
* @param keys must not be {@literal null}.
* @return the number of keys existing among the ones specified as arguments.
* @since 2.1
*/
@Nullable
Long exists(Collection<byte[]> keys);
/**
* Delete given {@code keys}.

View File

@@ -91,6 +91,18 @@ public interface StringRedisConnection extends RedisConnection {
*/
Boolean exists(String key);
/**
* Count how many of the if given {@code keys} exists.
*
* @param keys must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/exists">Redis Documentation: EXISTS</a>
* @see RedisKeyCommands#exists(java.util.Collection)
* @since 2.1
*/
@Nullable
Long exists(String... keys);
/**
* Delete given {@code keys}.
*

View File

@@ -37,8 +37,10 @@ import org.springframework.data.redis.connection.jedis.JedisClusterConnection.Je
import org.springframework.data.redis.connection.jedis.JedisClusterConnection.JedisMultiKeyClusterCommandCallback;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
/**
* @author Christoph Strobl
@@ -471,15 +473,23 @@ class JedisClusterKeyCommands implements RedisKeyCommands {
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#exists(byte[])
* @see org.springframework.data.redis.connection.RedisKeyCommands#exists(java.util.Collection)
*/
@Nullable
@Override
public Boolean exists(byte[] key) {
public Long exists(Collection<byte[]> keys) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(keys, "Keys must not be null!");
try {
return connection.getCluster().exists(key);
return keys.stream() //
.parallel()
.map(key -> connection.getClusterCommandExecutor()
.executeCommandOnSingleNode((JedisClusterCommandCallback<Boolean>) client -> client.exists(key),
connection.getTopologyProvider().getTopology().getKeyServingMasterNode(key))
.getValue())
.mapToLong(val -> ObjectUtils.nullSafeEquals(val, Boolean.TRUE) ? 1L : 0L).sum();
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}

View File

@@ -20,6 +20,7 @@ import lombok.RequiredArgsConstructor;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.SortingParams;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -70,6 +71,33 @@ class JedisKeyCommands implements RedisKeyCommands {
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#exists(java.util.Collection)
*/
@Nullable
@Override
public Long exists(Collection<byte[]> keys) {
Assert.notNull(keys, "Keys must not be null!");
try {
if (isPipelined()) {
pipeline(
connection.newJedisResult(connection.getRequiredPipeline().exists(keys.toArray(new byte[keys.size()][]))));
return null;
}
if (isQueueing()) {
transaction(connection
.newJedisResult(connection.getRequiredTransaction().exists(keys.toArray(new byte[keys.size()][]))));
return null;
}
return connection.getJedis().exists(keys.toArray(new byte[keys.size()][]));
} catch (Exception ex) {
throw connection.convertJedisAccessException(ex);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#del(byte[][])

View File

@@ -23,6 +23,7 @@ import io.lettuce.core.cluster.api.sync.RedisClusterCommands;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -38,6 +39,7 @@ 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.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -76,6 +78,31 @@ class LettuceKeyCommands implements RedisKeyCommands {
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#exists(java.util.Collection)
*/
@Nullable
@Override
public Long exists(Collection<byte[]> keys) {
Assert.notNull(keys, "Keys must not be null!");
try {
if (isPipelined()) {
pipeline(connection.newLettuceResult(getAsyncConnection().exists(keys.toArray(new byte[keys.size()][]))));
return null;
}
if (isQueueing()) {
transaction(connection.newLettuceTxResult(getAsyncConnection().exists(keys.toArray(new byte[keys.size()][]))));
return null;
}
return getConnection().exists(keys.toArray(new byte[keys.size()][]));
} catch (Exception ex) {
throw convertLettuceAccessException(ex);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#del(byte[][])

View File

@@ -162,6 +162,18 @@ public interface RedisOperations<K, V> {
@Nullable
Boolean hasKey(K key);
/**
* Count the number of {@code keys} that exists.
*
* @param keys must not be {@literal null}.
* @return The number of keys existing among the ones specified as arguments. Keys mentioned multiple times and
* existing are counted multiple times.
* @see <a href="http://redis.io/commands/exists">Redis Documentation: EXISTS</a>
* @since 2.1
*/
@Nullable
Long countExistingKeys(Collection<K> keys);
/**
* Delete given {@code key}.
*

View File

@@ -18,6 +18,7 @@ package org.springframework.data.redis.core;
import java.io.Closeable;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -739,6 +740,19 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
return execute(connection -> connection.exists(rawKey), true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.RedisOperations#countExistingKeys(java.util.Collection)
*/
@Override
public Long countExistingKeys(Collection<K> keys) {
Assert.notNull(keys, "Keys must not be null!");
byte[][] rawKeys = rawKeys(keys);
return execute(connection -> connection.exists(Arrays.asList(rawKeys)), true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.RedisOperations#expire(java.lang.Object, long, java.util.concurrent.TimeUnit)