Skip to content
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

Refine JedisConnectionFactory #2746

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

Refine JedisConnectionFactory #2746

wants to merge 2 commits into from

Conversation

jxblum
Copy link
Contributor

@jxblum jxblum commented Oct 18, 2023

See #2745

Copy link
Member

@mp911de mp911de left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a first review pass. The majority of changes impose a different style, resulting in code that requires more effort to read (harder to read) and that isn't an improvement.

@@ -100,18 +102,17 @@ public class JedisConnectionFactory

private static final Log log = LogFactory.getLog(JedisConnectionFactory.class);

private static final ExceptionTranslationStrategy EXCEPTION_TRANSLATION = new PassThroughExceptionTranslationStrategy(
JedisExceptionConverter.INSTANCE);
private static final ExceptionTranslationStrategy EXCEPTION_TRANSLATION = jedisExceptionTranslationStrategy();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introducing a indirection makes it difficult to understand at first sight the details.

Copy link
Contributor Author

@jxblum jxblum Oct 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoiding line breaks and undue noise. Plus, details aren't needed until they are needed. It is easy to navigate into the method.

@@ -125,8 +126,7 @@ public class JedisConnectionFactory

private @Nullable RedisConfiguration configuration;

private RedisStandaloneConfiguration standaloneConfig = new RedisStandaloneConfiguration("localhost",
Protocol.DEFAULT_PORT);
private RedisStandaloneConfiguration standaloneConfig = defaultStandaloneConfiguration();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introducing a indirection makes it difficult to understand at first sight the details.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same reason as above.

@@ -249,8 +249,7 @@ public JedisConnectionFactory(RedisSentinelConfiguration sentinelConfiguration,
@Nullable JedisPoolConfig poolConfig) {

this.configuration = sentinelConfiguration;
this.clientConfiguration = MutableJedisClientConfiguration
.create(poolConfig != null ? poolConfig : new JedisPoolConfig());
this.clientConfiguration = MutableJedisClientConfiguration.create(nullSafePoolConfig(poolConfig));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inflationary usage of nullSafe… methods creates a lot of noise hiding the actual details. Introducing indirections makes it difficult to understand at first sight the details.

Copy link
Contributor Author

@jxblum jxblum Oct 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lines in question are too busy creating an 1) incomplete/break-in statement and 2) unnecessary line break. They are also redundant.

Plus, "named" methods are more intuitive and this method (nullSafePoolConfig(..)) is reused.

Copy link
Contributor Author

@jxblum jxblum Oct 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this is not unlike Spring Framework's own nullSafeClassName(..) (Javadoc) and nullSafeConciseToString(..) (Javadoc) methods, or the countless other nullSafe... variants.

Your reference to "indirections" is a bit ironic.

this.pool = null;
} catch (Exception ex) {
log.warn("Cannot properly close Jedis pool", ex);
} catch (Exception cause) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be precise: cause isn't true as the cause refers to the exception cause and not the caught exception. Please stick to ex.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cause is the reason the error occurred in the block above. You are confusing this with caused by.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any case, I am not going to argue about this.

If you want it to be consistent, then we should call it e everywhere Exceptions are caught (or ignore when Exceptions are not handled), which is what the core Spring Framework uses (i.e. e, not sure about ignore).

Using ex is not consistent, and o_O is not professional.

static class MutableJedisClientConfiguration implements JedisClientConfiguration {

private boolean useSsl;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason to tear code with good locality apart? All SSL-related fields have been kept together. Now, these are spread across.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Organization by type. All the state variables were bunched together making it difficult to read.

If you want better organization around concern, then perhaps a private, internal SslConfiguration class used by MutableJedisClientConfiguration makes more sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted for the time being. This has room for improvement.

Copy link
Contributor Author

@jxblum jxblum Oct 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I see where we get our influence:

package redis.clients.jedis;

import ...;

public final class DefaultJedisClientConfig implements JedisClientConfig {

  private final RedisProtocol redisProtocol;

  private final int connectionTimeoutMillis;
  private final int socketTimeoutMillis;
  private final int blockingSocketTimeoutMillis;

  private volatile Supplier<RedisCredentials> credentialsProvider;
  private final int database;
  private final String clientName;

  private final boolean ssl;
  private final SSLSocketFactory sslSocketFactory;
  private final SSLParameters sslParameters;
  private final HostnameVerifier hostnameVerifier;

  //...

This is unfortunate. And, this is only marginally better.

} catch (Exception ex) {
log.warn(String.format("Ping failed for sentinel host: %s", node.getHost()), ex);
} catch (Exception cause) {
log.warn("Ping failed for sentinel host: %s".formatted(node.getHost()), cause);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please let's remain on String.format(…) to remain consitent. We can change all occurrences of String.format(…) to formatted if we feel that the value we get from that outweighs the effort.

Copy link
Contributor Author

@jxblum jxblum Oct 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"some string with placeholders".formatted(..) is less noisy and it makes the "message" more apparent.

Why change try-finally blocks with proper handling to try-with-resources blocks, or use for-each loops when for(index; condition; increment) works perfectly fine?

Simple. Because it reads easier, or in the case of try-with-resources, is a more reliable alternative and welcome language feature.

NOTE: Even multi-line Strings would be a nice addition in certain places where we use really long Exception messages.

I don't believe in big bang, change everything at once and be done with it. All improvements happen incrementally or generally don't/won't happen at all. They are not all or nothing.

In addition, where String formatting, or even concatenation, are concerned, String.format(..) is not even consistently used everywhere, and not just Spring Data Redis.

Copy link
Contributor Author

@jxblum jxblum Oct 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Case in point: https://github.com/spring-projects/spring-data-redis/blob/3.2.0-RC1/src/main/java/org/springframework/data/redis/core/script/DigestUtils.java#L63

Seems there was already a style in place that predates the existing style. So, either 1) we do not consistently follow any standard or 2) authors "come up with our own way" and then don't consistently apply it everywhere, which enforces my point about "big bang" changes don't work.

Copy link
Contributor Author

@jxblum jxblum Oct 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another case in point: https://github.com/spring-projects/spring-data-redis/blob/3.2.0-RC1/src/main/java/org/springframework/data/redis/core/mapping/RedisMappingContext.java#L245-L250

In the RedisMappintContext's case, inconsistent and incorrect message formatting was used.

I am certain there are others.

if (jedis.ping().equalsIgnoreCase("pong")) {
success = true;
return jedis;
}
} catch (Exception ex) {
log.warn(String.format("Ping failed for sentinel host: %s", node.getHost()), ex);
} catch (Exception cause) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cause is technically wrong, please stick to ex

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. It is the cause for the Exception being thrown in the block above.

* @param cluster reference to the configured {@link JedisCluster} used by the {@link ClusterTopologyProvider}
* to interact with the Redis cluster; must not be {@literal null}.
* @return a new {@link ClusterTopologyProvider}.
* @see org.springframework.data.redis.connection.jedis.JedisClusterConnection.JedisClusterTopologyProvider
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to create duplicate @see references to types that are already present in the signature.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed

* @see redis.clients.jedis.JedisCluster
* @since 3.2
*/
protected ClusterCommandExecutor createClusterCommandExecutor(JedisCluster cluster,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to create duplicate @see references to types that are already present in the signature.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed

*/
@Deprecated(since = "3.2")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand what warrants a deprecation of the createTopologyProvider. New deprecations are a signal for existing users to do something about their usage while we do not solve an actual problem.

Copy link
Contributor Author

@jxblum jxblum Oct 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API and naming consistency with other cluster-oriented types, along with the type itself, ClusterTopologyProvider.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was quite surprised when I first learned about Redis that we don't have something equivalent to SentinelTopologyProvider for Redis Sentinel.

Of course, Redis Sentinel is not a fully managed, "clustered" solution (only HA), but is a group of nodes in a distributed system none-the-less.

Technically, ClusterTopology is Set of RedisClusterNode and similarly, RedisSentinelConfiguration contains a Set of RedisNode where RedisClusterNode extends from RedisNode. So, although they are not quite the same, they are similar enough.

Introduces private [static] methods (and/or local variables as needed) to simplify and remove duplicate logic while simultaneously removing broken line breaks affecting readability and understanding.

Simplifies assertions by making consistent use of Spring Frameworks Assert facility rather than unnecessary conditional blocks.

Refers to state variables using getter (rather than direct variable reference) where applicable helping to improve extensibility.

Deprecates createTopologyProvider(..) in favor of createClusterTopologyProvider(..) for consistent and clarified naming.

Cleans up compiler warnings.

Closes #2745
@jxblum
Copy link
Contributor Author

jxblum commented Oct 18, 2023

Did a first review pass. The majority of changes impose a different style, resulting in code that requires more effort to read (harder to read) and that isn't an improvement.

I reworked this PR.

Please keep your comments objective and constructive.

If you re-read the commit message, you will notice several areas where the code was "improved". Cleaning up compiler warnings is a worthwhile improvement all by itself.

Finally, if you don't agree with the deprecation of the "protected" createTopologyProvider(..) (replaced by the new createClusterTopologyProvider(..) method). I don't have strong objections here. I just think it is better, more consistent from naming perspective. Our naming across the API is inconsistent, sometimes referring to the same types, but using different names.

@jxblum jxblum changed the title Refine JedisConnectionFactory Refine JedisConnectionFactory Oct 19, 2023
@mp911de mp911de removed this from the 3.2 GA (2023.1.0) milestone Nov 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: jedis Jedis driver type: task A general task
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants