Skip to content

Upgrade to Jedis 4.2 #2287

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>3.0.0-SNAPSHOT</version>
<version>3.0.0-GH-2212-SNAPSHOT</version>

<name>Spring Data Redis</name>

Expand All @@ -20,9 +20,9 @@
<awaitility>4.0.2</awaitility>
<beanutils>1.9.4</beanutils>
<xstream>1.4.19</xstream>
<pool>2.9.0</pool>
<pool>2.11.1</pool>
<lettuce>6.1.8.RELEASE</lettuce>
<jedis>3.8.0</jedis>
<jedis>4.2.0</jedis>
<multithreadedtc>1.01</multithreadedtc>
<netty>4.1.72.Final</netty>
<java-module-name>spring.data.redis</java-module-name>
Expand Down
7 changes: 6 additions & 1 deletion src/main/asciidoc/new-features.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@

This section briefly covers items that are new and noteworthy in the latest releases.

[[new-in-2.7.0]]
[[new-in-3.0.0]]
== New in Spring Data Redis 3.0

* Upgrade to Jedis 4.1. Jedis 4 imposes certain limitations on transactions and pipelines and commands used in pipelines/transactions, see <<redis:connectors:overview>>.


== New in Spring Data Redis 2.7

* Sentinel ACL authentication considering a sentinel-specific username. Setting a username enables username and password authentication requiring Redis 6.
Expand Down
2 changes: 1 addition & 1 deletion src/main/asciidoc/reference/redis-cluster.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class AppConfig {

public @Bean RedisConnectionFactory connectionFactory() {

return new JedisConnectionFactory(
return new LettuceConnectionFactory(
new RedisClusterConfiguration(clusterProperties.getNodes()));
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/main/asciidoc/reference/redis-repositories.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public class ApplicationConfig {

@Bean
public RedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
return new LettuceConnectionFactory();
}

@Bean
Expand Down Expand Up @@ -843,10 +843,10 @@ class RedisOperationsProducer {
@Produces
RedisConnectionFactory redisConnectionFactory() {

JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(new RedisStandaloneConfiguration());
jedisConnectionFactory.afterPropertiesSet();
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(new RedisStandaloneConfiguration());
connectionFactory.afterPropertiesSet();

return jedisConnectionFactory;
return connectionFactory;
}

void disposeRedisConnectionFactory(@Disposes RedisConnectionFactory redisConnectionFactory) throws Exception {
Expand Down
29 changes: 14 additions & 15 deletions src/main/asciidoc/reference/redis.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ Unfortunately, currently, not all connectors support all Redis features. When in

| Other Connection Features
| Singleton-connection sharing for non-blocking commands
| `JedisShardInfo` support
| Pipelining and Transactions mutually exclusive. Cannot use server/connection commands in pipeline/transactions.

| SSL Support
| X
Expand All @@ -130,11 +130,11 @@ Unfortunately, currently, not all connectors support all Redis features. When in

| <<pipeline,Pipelining>>
| X
| X
| X (Pipelining and Transactions mutually exclusive)

| <<tx,Transactions>>
| X
| X
| X (Pipelining and Transactions mutually exclusive)

| Datatype support
| Key, String, List, Set, Sorted Set, Hash, Server, Stream, Scripting, Geo, HyperLogLog
Expand Down Expand Up @@ -204,7 +204,7 @@ NOTE: Netty currently supports the epoll (Linux) and kqueue (BSD/macOS) interfac
[[redis:connectors:jedis]]
=== Configuring the Jedis Connector

https://github.com/xetorthio/jedis[Jedis] is a community-driven connector supported by the Spring Data Redis module through the `org.springframework.data.redis.connection.jedis` package.
https://github.com/redis/jedis[Jedis] is a community-driven connector supported by the Spring Data Redis module through the `org.springframework.data.redis.connection.jedis` package.


.Add the following to the pom.xml files `dependencies` element:
Expand All @@ -223,7 +223,6 @@ https://github.com/xetorthio/jedis[Jedis] is a community-driven connector suppor
</dependencies>
----


In its simplest form, the Jedis configuration looks as follow:

[source,java]
Expand Down Expand Up @@ -288,27 +287,27 @@ For dealing with high-availability Redis, Spring Data Redis has support for http
[source,java]
----
/**
* Jedis
* Lettuce
*/
@Bean
public RedisConnectionFactory jedisConnectionFactory() {
public RedisConnectionFactory lettuceConnectionFactory() {
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
.master("mymaster")
.sentinel("127.0.0.1", 26379)
.sentinel("127.0.0.1", 26380);
return new JedisConnectionFactory(sentinelConfig);
return new LettuceConnectionFactory(sentinelConfig);
}

/**
* Lettuce
* Jedis
*/
@Bean
public RedisConnectionFactory lettuceConnectionFactory() {
public RedisConnectionFactory jedisConnectionFactory() {
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
.master("mymaster")
.sentinel("127.0.0.1", 26379)
.sentinel("127.0.0.1", 26380);
return new LettuceConnectionFactory(sentinelConfig);
return new JedisConnectionFactory(sentinelConfig);
}
----

Expand Down Expand Up @@ -400,9 +399,9 @@ For cases where you need a certain template view, declare the view as a dependen
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true"/>
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
<!-- redis template definition -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory"/>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="redisConnectionFactory"/>
...

</beans>
Expand Down Expand Up @@ -439,9 +438,9 @@ Since it is quite common for the keys and values stored in Redis to be `java.lan
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true"/>
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>

<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="jedisConnectionFactory"/>
<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="redisConnectionFactory"/>
...
</beans>
----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,8 @@
* @author Mark Paluch
* @author Christoph Strobl
*/
abstract public class Converters {
public abstract class Converters {

// private static final Log LOGGER = LogFactory.getLog(Converters.class);
private static final Log LOGGER = LogFactory.getLog(Converters.class);

private static final byte[] ONE = new byte[] { '1' };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.connection.convert;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -48,4 +49,8 @@ public Set<T> convert(Set<S> source) {
return source.stream().map(itemConverter::convert).collect(Collectors.toCollection(LinkedHashSet::new));
}

public Set<T> convert(Collection<S> source) {
return source.stream().map(itemConverter::convert).collect(Collectors.toCollection(LinkedHashSet::new));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,13 @@
*/
package org.springframework.data.redis.connection.jedis;

import redis.clients.jedis.Builder;
import redis.clients.jedis.Client;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Protocol.Command;
import redis.clients.jedis.Queable;
import redis.clients.jedis.Response;
import redis.clients.jedis.util.SafeEncoder;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.commands.ProtocolCommand;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.springframework.util.ReflectionUtils;

/**
* Utility class to dispatch arbitrary Redis commands using Jedis commands.
*
Expand All @@ -43,128 +33,23 @@
@SuppressWarnings({ "unchecked", "ConstantConditions" })
class JedisClientUtils {

private static final Method GET_RESPONSE;
private static final Set<String> KNOWN_COMMANDS;
private static final Builder<Object> OBJECT_BUILDER;

static {

GET_RESPONSE = ReflectionUtils.findMethod(Queable.class, "getResponse", Builder.class);
ReflectionUtils.makeAccessible(GET_RESPONSE);

KNOWN_COMMANDS = Arrays.stream(Command.values()).map(Enum::name).collect(Collectors.toSet());

OBJECT_BUILDER = new Builder<Object>() {
public Object build(Object data) {
return data;
}

public String toString() {
return "Object";
}
};
}

/**
* Execute an arbitrary on the supplied {@link Jedis} instance.
*
* @param command the command.
* @param keys must not be {@literal null}, may be empty.
* @param args must not be {@literal null}, may be empty.
* @param jedis must not be {@literal null}.
* @return the response, can be be {@literal null}.
*/
static <T> T execute(String command, byte[][] keys, byte[][] args, Supplier<Jedis> jedis) {
return execute(command, keys, args, jedis, it -> (T) it.getOne());
}

/**
* Execute an arbitrary on the supplied {@link Jedis} instance.
*
* @param command the command.
* @param keys must not be {@literal null}, may be empty.
* @param args must not be {@literal null}, may be empty.
* @param jedis must not be {@literal null}.
* @param responseMapper must not be {@literal null}.
* @return the response, can be be {@literal null}.
* @since 2.1
*/
static <T> T execute(String command, byte[][] keys, byte[][] args, Supplier<Jedis> jedis,
Function<Client, T> responseMapper) {

byte[][] commandArgs = getCommandArguments(keys, args);

Client client = sendCommand(command, commandArgs, jedis.get());

return responseMapper.apply(client);
KNOWN_COMMANDS = Arrays.stream(Protocol.Command.values()).map(Enum::name).collect(Collectors.toSet());
}

/**
* Send a Redis command and retrieve the {@link Client} for response retrieval.
*
* @param command the command.
* @param args must not be {@literal null}, may be empty.
* @param jedis must not be {@literal null}.
* @return the {@link Client} instance used to send the command.
*/
static Client sendCommand(String command, byte[][] args, Jedis jedis) {

Client client = jedis.getClient();

sendCommand(client, command, args);

return client;
}

private static void sendCommand(Client client, String command, byte[][] args) {
public static ProtocolCommand getCommand(String command) {

if (isKnownCommand(command)) {
client.sendCommand(Command.valueOf(command.trim().toUpperCase()), args);
} else {
client.sendCommand(() -> SafeEncoder.encode(command.trim().toUpperCase()), args);
return Protocol.Command.valueOf(command.trim().toUpperCase());
}

return () -> JedisConverters.toBytes(command);
}

private static boolean isKnownCommand(String command) {
return KNOWN_COMMANDS.contains(command);
}

private static byte[][] getCommandArguments(byte[][] keys, byte[][] args) {

if (keys.length == 0) {
return args;
}

if (args.length == 0) {
return keys;
}

byte[][] commandArgs = new byte[keys.length + args.length][];

System.arraycopy(keys, 0, commandArgs, 0, keys.length);
System.arraycopy(args, 0, commandArgs, keys.length, args.length);

return commandArgs;
}

/**
* @param jedis the client instance.
* @return {@literal true} if the connection has entered {@literal MULTI} state.
*/
static boolean isInMulti(Jedis jedis) {
return jedis.getClient().isInMulti();
}

/**
* Retrieve the {@link Response} object from a {@link redis.clients.jedis.Transaction} or a
* {@link redis.clients.jedis.Pipeline} for response synchronization.
*
* @param target a {@link redis.clients.jedis.Transaction} or {@link redis.clients.jedis.Pipeline}, must not be
* {@literal null}.
* @return the {@link Response} wrapper object.
*/
static Response<Object> getResponse(Object target) {
return (Response<Object>) ReflectionUtils.invokeMethod(GET_RESPONSE, target, OBJECT_BUILDER);
}

}
Loading