From 556474e9a09fbe41b4978db3f76082c9cbfee0ba Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 15 Aug 2023 16:56:15 +0200 Subject: [PATCH] Register library name and library version on Redis 7.2 or greater #2483 RedisURI now accepts library name and library version and sets those after the handshake. --- pom.xml | 22 +++ .../io/lettuce/core/ConnectionMetadata.java | 74 ++++++++ .../java/io/lettuce/core/ConnectionState.java | 16 +- .../java/io/lettuce/core/LettuceVersion.java | 50 ++++++ .../java/io/lettuce/core/RedisClient.java | 13 +- .../java/io/lettuce/core/RedisHandshake.java | 134 ++++++++++++++- src/main/java/io/lettuce/core/RedisURI.java | 159 +++++++++++++++++- .../core/AuthenticationIntegrationTests.java | 2 +- .../core/RedisURIBuilderUnitTests.java | 12 ++ .../io/lettuce/core/RedisURIUnitTests.java | 9 + .../ClusterClientOptionsIntegrationTests.java | 2 +- .../NodeSelectionSyncIntegrationTests.java | 2 + ...SentinelServerCommandIntegrationTests.java | 18 -- .../tracing/BraveTracingIntegrationTests.java | 17 +- 14 files changed, 484 insertions(+), 46 deletions(-) create mode 100644 src/main/java/io/lettuce/core/ConnectionMetadata.java create mode 100644 src/main/java/io/lettuce/core/LettuceVersion.java diff --git a/pom.xml b/pom.xml index 34f613cc03..98ef179970 100644 --- a/pom.xml +++ b/pom.xml @@ -551,6 +551,12 @@ 1.3.0 + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + + org.apache.maven.plugins maven-assembly-plugin @@ -658,6 +664,19 @@ + + pl.project13.maven + git-commit-id-plugin + + + get-the-git-infos + + revision + + initialize + + + org.codehaus.mojo @@ -796,6 +815,9 @@ + + ${project.version}/${git.commit.id.abbrev} + lettuce.core diff --git a/src/main/java/io/lettuce/core/ConnectionMetadata.java b/src/main/java/io/lettuce/core/ConnectionMetadata.java new file mode 100644 index 0000000000..672ef37aaf --- /dev/null +++ b/src/main/java/io/lettuce/core/ConnectionMetadata.java @@ -0,0 +1,74 @@ +/* + * Copyright 2023 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 + * + * https://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 io.lettuce.core; + +/** + * @author Mark Paluch + */ +class ConnectionMetadata { + + private volatile String clientName; + + private volatile String libraryName; + + private volatile String libraryVersion; + + public ConnectionMetadata() { + } + + public ConnectionMetadata(RedisURI uri) { + apply(uri); + } + + public void apply(RedisURI redisURI) { + + setClientName(redisURI.getClientName()); + setLibraryName(redisURI.getLibraryName()); + setLibraryVersion(redisURI.getLibraryVersion()); + } + + public void apply(ConnectionMetadata metadata) { + + setClientName(metadata.getClientName()); + setLibraryName(metadata.getLibraryName()); + setLibraryVersion(metadata.getLibraryVersion()); + } + + protected void setClientName(String clientName) { + this.clientName = clientName; + } + + String getClientName() { + return clientName; + } + + void setLibraryName(String libraryName) { + this.libraryName = libraryName; + } + + String getLibraryName() { + return libraryName; + } + + void setLibraryVersion(String libraryVersion) { + this.libraryVersion = libraryVersion; + } + + String getLibraryVersion() { + return libraryVersion; + } + +} diff --git a/src/main/java/io/lettuce/core/ConnectionState.java b/src/main/java/io/lettuce/core/ConnectionState.java index 2e1d7d71ad..513c29c5a8 100644 --- a/src/main/java/io/lettuce/core/ConnectionState.java +++ b/src/main/java/io/lettuce/core/ConnectionState.java @@ -37,7 +37,7 @@ public class ConnectionState { private volatile boolean readOnly; - private volatile String clientName; + private volatile ConnectionMetadata connectionMetadata = new ConnectionMetadata(); /** * Applies settings from {@link RedisURI}. @@ -46,10 +46,18 @@ public class ConnectionState { */ public void apply(RedisURI redisURI) { - setClientName(redisURI.getClientName()); + connectionMetadata.apply(redisURI); setCredentialsProvider(redisURI.getCredentialsProvider()); } + void apply(ConnectionMetadata metadata) { + this.connectionMetadata.apply(metadata); + } + + ConnectionMetadata getConnectionMetadata() { + return connectionMetadata; + } + /** * Returns the negotiated {@link ProtocolVersion}. * @@ -142,11 +150,11 @@ boolean isReadOnly() { } protected void setClientName(String clientName) { - this.clientName = clientName; + this.connectionMetadata.setClientName(clientName); } String getClientName() { - return clientName; + return connectionMetadata.getClientName(); } /** diff --git a/src/main/java/io/lettuce/core/LettuceVersion.java b/src/main/java/io/lettuce/core/LettuceVersion.java new file mode 100644 index 0000000000..e33d01dcfb --- /dev/null +++ b/src/main/java/io/lettuce/core/LettuceVersion.java @@ -0,0 +1,50 @@ +/* + * Copyright 2023 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 + * + * https://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 io.lettuce.core; + +/** + * Class that exposes the Lettuce version. Fetches the "Implementation-Version" manifest attribute from the jar file. + *

+ * Note that some ClassLoaders do not expose the package metadata, hence this class might not be able to determine the Lettuce + * version in all environments. Consider using a reflection-based check instead — for example, checking for the presence + * of a specific Lettuce method that you intend to call. + * + * @author Mark Paluch + * @since 6.3 + */ +public final class LettuceVersion { + + private LettuceVersion() { + } + + /** + * Return the library name. + */ + public static String getName() { + return "Lettuce"; + } + + /** + * Return the full version string of the present Lettuce codebase, or {@code null} if it cannot be determined. + * + * @see Package#getImplementationVersion() + */ + public static String getVersion() { + Package pkg = LettuceVersion.class.getPackage(); + return (pkg != null ? pkg.getImplementationVersion() : null); + } + +} diff --git a/src/main/java/io/lettuce/core/RedisClient.java b/src/main/java/io/lettuce/core/RedisClient.java index 461986d94d..110895f1f3 100644 --- a/src/main/java/io/lettuce/core/RedisClient.java +++ b/src/main/java/io/lettuce/core/RedisClient.java @@ -28,14 +28,12 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.function.Supplier; -import reactor.core.publisher.Mono; import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.codec.RedisCodec; import io.lettuce.core.codec.StringCodec; import io.lettuce.core.internal.ExceptionFactory; import io.lettuce.core.internal.Futures; import io.lettuce.core.internal.LettuceAssert; -import io.lettuce.core.internal.LettuceStrings; import io.lettuce.core.masterreplica.MasterReplica; import io.lettuce.core.protocol.CommandExpiryWriter; import io.lettuce.core.protocol.CommandHandler; @@ -51,6 +49,7 @@ import io.lettuce.core.sentinel.api.StatefulRedisSentinelConnection; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; +import reactor.core.publisher.Mono; /** * A scalable and thread-safe Redis client supporting synchronous, asynchronous and reactive @@ -510,7 +509,7 @@ private CompletableFuture> connectS logger.debug("Trying to get a Redis Sentinel connection for one of: " + redisURI.getSentinels()); if (redisURI.getSentinels().isEmpty() && (isNotEmpty(redisURI.getHost()) || !isEmpty(redisURI.getSocket()))) { - return doConnectSentinelAsync(codec, redisURI, timeout, redisURI.getClientName()).toCompletableFuture(); + return doConnectSentinelAsync(codec, redisURI, timeout, new ConnectionMetadata(redisURI)).toCompletableFuture(); } List sentinels = redisURI.getSentinels(); @@ -522,7 +521,7 @@ private CompletableFuture> connectS for (RedisURI uri : sentinels) { Mono> connectionMono = Mono - .fromCompletionStage(() -> doConnectSentinelAsync(codec, uri, timeout, redisURI.getClientName())) + .fromCompletionStage(() -> doConnectSentinelAsync(codec, uri, timeout, new ConnectionMetadata(redisURI))) .onErrorMap(CompletionException.class, Throwable::getCause) .onErrorMap(e -> new RedisConnectionException("Cannot connect Redis Sentinel at " + uri, e)) .doOnError(exceptionCollector::add); @@ -557,7 +556,7 @@ private CompletableFuture> connectS } private ConnectionFuture> doConnectSentinelAsync(RedisCodec codec, - RedisURI redisURI, Duration timeout, String clientName) { + RedisURI redisURI, Duration timeout, ConnectionMetadata metadata) { ConnectionBuilder connectionBuilder; if (redisURI.isSsl()) { @@ -585,9 +584,7 @@ private ConnectionFuture> doConnect ConnectionState state = connection.getConnectionState(); state.apply(redisURI); - if (LettuceStrings.isEmpty(state.getClientName())) { - state.setClientName(clientName); - } + state.apply(metadata); connectionBuilder.connectionInitializer(createHandshake(state)); diff --git a/src/main/java/io/lettuce/core/RedisHandshake.java b/src/main/java/io/lettuce/core/RedisHandshake.java index 979774ad85..e608803682 100644 --- a/src/main/java/io/lettuce/core/RedisHandshake.java +++ b/src/main/java/io/lettuce/core/RedisHandshake.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; @@ -41,6 +42,8 @@ */ class RedisHandshake implements ConnectionInitializer { + private static final RedisVersion CLIENT_SET_INFO_SINCE = RedisVersion.of("7.2"); + private final RedisCommandBuilder commandBuilder = new RedisCommandBuilder<>(StringCodec.UTF8); private final ProtocolVersion requestedProtocolVersion; @@ -89,7 +92,8 @@ public CompletionStage initialize(Channel channel) { new RedisConnectionException("Protocol version" + this.requestedProtocolVersion + " not supported")); } - return handshake.thenCompose(ignore -> applyPostHandshake(channel, getNegotiatedProtocolVersion())); + return handshake.thenCompose( + ignore -> applyPostHandshake(channel, connectionState.getRedisVersion(), getNegotiatedProtocolVersion())); } private CompletionStage tryHandshakeResp3(Channel channel) { @@ -226,14 +230,35 @@ private AsyncCommand> dispatchHello(Channel return dispatch(channel, this.commandBuilder.hello(3, null, null, connectionState.getClientName())); } - private CompletableFuture applyPostHandshake(Channel channel, ProtocolVersion negotiatedProtocolVersion) { + private CompletableFuture applyPostHandshake(Channel channel, String redisVersion, + ProtocolVersion negotiatedProtocolVersion) { List> postHandshake = new ArrayList<>(); - if (connectionState.getClientName() != null && negotiatedProtocolVersion == ProtocolVersion.RESP2) { + ConnectionMetadata metadata = connectionState.getConnectionMetadata(); + + if (metadata.getClientName() != null && negotiatedProtocolVersion == ProtocolVersion.RESP2) { postHandshake.add(new AsyncCommand<>(this.commandBuilder.clientSetname(connectionState.getClientName()))); } + if (negotiatedProtocolVersion == ProtocolVersion.RESP3) { + + RedisVersion currentVersion = RedisVersion.of(redisVersion); + + if (currentVersion.isGreaterThanOrEqualTo(CLIENT_SET_INFO_SINCE)) { + + if (LettuceStrings.isNotEmpty(metadata.getLibraryName())) { + postHandshake + .add(new AsyncCommand<>(this.commandBuilder.clientSetinfo("lib-name", metadata.getLibraryName()))); + } + + if (LettuceStrings.isNotEmpty(metadata.getLibraryVersion())) { + postHandshake.add( + new AsyncCommand<>(this.commandBuilder.clientSetinfo("lib-ver", metadata.getLibraryVersion()))); + } + } + } + if (connectionState.getDb() > 0) { postHandshake.add(new AsyncCommand<>(this.commandBuilder.select(connectionState.getDb()))); } @@ -279,4 +304,107 @@ private static boolean isNoProto(Throwable error) { && error.getMessage().startsWith("NOPROTO"); } + /** + * Value object to represent a Redis version. + */ + static class RedisVersion { + + private final static RedisVersion UNKNOWN = new RedisVersion("0.0.0"); + + private final static RedisVersion UNSTABLE = new RedisVersion("255.255.255"); + + private final int major; + + private final int minor; + + private final int bugfix; + + private RedisVersion(String version) { + + String[] split = version.split("\\."); + + int major = 0; + int minor = 0; + int bugfix = 0; + if (split.length > 0) { + major = Integer.parseInt(split[0]); + } + if (split.length > 1) { + minor = Integer.parseInt(split[1]); + } + + if (split.length > 2) { + bugfix = Integer.parseInt(split[2]); + } + + this.major = major; + this.minor = minor; + this.bugfix = bugfix; + } + + /** + * Construct a new {@link RedisVersion} from a version string containing major, minor and bugfix version such as + * {@code 7.2.0}. + * + * @param version + * @return + */ + public static RedisVersion of(String version) { + return new RedisVersion(version); + } + + public boolean isGreaterThan(RedisVersion version) { + return this.compareTo(version) > 0; + } + + public boolean isGreaterThanOrEqualTo(RedisVersion version) { + return this.compareTo(version) >= 0; + } + + public boolean is(RedisVersion version) { + return this.equals(version); + } + + public boolean isLessThan(RedisVersion version) { + return this.compareTo(version) < 0; + } + + public boolean isLessThanOrEqualTo(RedisVersion version) { + return this.compareTo(version) <= 0; + } + + public int compareTo(RedisVersion that) { + if (this.major != that.major) { + return this.major - that.major; + } else if (this.minor != that.minor) { + return this.minor - that.minor; + } else { + return this.bugfix - that.bugfix; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RedisVersion that = (RedisVersion) o; + return major == that.major && minor == that.minor && bugfix == that.bugfix; + } + + @Override + public int hashCode() { + return Objects.hash(major, minor, bugfix); + } + + @Override + public String toString() { + return major + "." + minor + "." + bugfix; + } + + } + } diff --git a/src/main/java/io/lettuce/core/RedisURI.java b/src/main/java/io/lettuce/core/RedisURI.java index 84cb266a3d..dad00a1502 100644 --- a/src/main/java/io/lettuce/core/RedisURI.java +++ b/src/main/java/io/lettuce/core/RedisURI.java @@ -75,24 +75,28 @@ * Redis Standalone

redis{@code ://}[[username{@code :}]password@]host * [{@code :} port][{@code /}database][{@code ?} * [timeout=timeout[d|h|m|s|ms|us|ns]] [ &database=database] [&clientName=clientName] - * [&verifyPeer=NONE|CA|FULL]]
+ * [&libraryName=libraryName] [&libraryVersion=libraryVersion] [&verifyPeer=NONE|CA|FULL]] + * * * Redis Standalone (SSL)
* rediss{@code ://}[[username{@code :}]password@]host [{@code :} * port][{@code /}database][{@code ?} [timeout=timeout[d|h|m|s|ms|us|ns]] [ - * &database=database] [&clientName=clientName] [&verifyPeer=NONE|CA|FULL]]
+ * &database=database] [&clientName=clientName] [&libraryName=libraryName] + * [&libraryVersion=libraryVersion] [&verifyPeer=NONE|CA|FULL]] * * Redis Standalone (Unix Domain Sockets)
redis-socket{@code ://} * [[username{@code :}]password@]path[ * {@code ?}[timeout=timeout[d|h|m|s|ms|us|ns]][&database=database] - * [&clientName=clientName] [&verifyPeer=NONE|CA|FULL]]
+ * [&clientName=clientName] [&libraryName=libraryName] [&libraryVersion=libraryVersion] + * [&verifyPeer=NONE|CA|FULL]] * * Redis Sentinel
* redis-sentinel{@code ://}[[username{@code :}]password@]host1 [{@code :} * port1][, host2 [{@code :}port2]][, hostN [{@code :}portN]][{@code /} * database][{@code ?} [timeout=timeout[d|h|m|s|ms|us|ns]] [ * &sentinelMasterId=sentinelMasterId] [&database=database] [&clientName=clientName] - * [&verifyPeer=NONE|CA|FULL]]
+ * [&libraryName=libraryName] [&libraryVersion=libraryVersion] [&verifyPeer=NONE|CA|FULL]] + * * * *

Schemes @@ -139,6 +143,8 @@ *
  • When using Redis Sentinel, the password from the URI applies to the data nodes only. Sentinel authentication must be * configured for each {@link #getSentinels() sentinel node}.
  • *
  • Usernames are supported as of Redis 6.
  • + *
  • Library name and library version are automatically set on Redis 7.2 or greater defaulting to + * {@link LettuceVersion#getVersion()}.
  • * * * @author Mark Paluch @@ -177,6 +183,10 @@ public class RedisURI implements Serializable, ConnectionPoint { public static final String PARAMETER_NAME_CLIENT_NAME = "clientName"; + public static final String PARAMETER_NAME_LIBRARY_NAME = "libraryName"; + + public static final String PARAMETER_NAME_LIBRARY_VERSION = "libraryVersion"; + public static final String PARAMETER_NAME_VERIFY_PEER = "verifyPeer"; public static final Map> CONVERTER_MAP; @@ -222,6 +232,10 @@ public class RedisURI implements Serializable, ConnectionPoint { private String clientName; + private String libraryName = LettuceVersion.getName(); + + private String libraryVersion = LettuceVersion.getVersion(); + @Deprecated private String username; @@ -327,6 +341,14 @@ public static Builder builder(RedisURI source) { builder.withClientName(source.getClientName()); } + if (source.getLibraryName() != null) { + builder.withLibraryName(source.getLibraryName()); + } + + if (source.getLibraryVersion() != null) { + builder.withLibraryVersion(source.getLibraryVersion()); + } + if (source.socket != null) { builder.socket = source.getSocket(); } else { @@ -616,6 +638,52 @@ public void setClientName(String clientName) { this.clientName = clientName; } + /** + * Returns the library name. + * + * @return the library name. + * @since 6.3 + */ + public String getLibraryName() { + return libraryName; + } + + /** + * Sets the library name to be applied on Redis connections. + * + * @param libraryName the library name. + * @since 4.4 + */ + public void setLibraryName(String libraryName) { + if (libraryName != null && libraryName.indexOf(' ') != -1) { + throw new IllegalArgumentException("Library name must not contain spaces"); + } + this.libraryName = libraryName; + } + + /** + * Returns the library version. + * + * @return the library version. + * @since 6.3 + */ + public String getLibraryVersion() { + return libraryVersion; + } + + /** + * Sets the library version to be applied on Redis connections. + * + * @param libraryVersion the library version. + * @since 4.4 + */ + public void setLibraryVersion(String libraryVersion) { + if (libraryVersion != null && libraryVersion.indexOf(' ') != -1) { + throw new IllegalArgumentException("Library version must not contain spaces"); + } + this.libraryVersion = libraryVersion; + } + /** * Apply authentication from another {@link RedisURI}. The SSL settings of the {@code source} URI will be applied to this * URI. That is in particular SSL usage, peer verification and StartTLS. @@ -815,6 +883,14 @@ private static RedisURI buildRedisUriFromUri(URI uri) { parseClientName(builder, queryParam); } + if (forStartWith.startsWith(PARAMETER_NAME_LIBRARY_NAME.toLowerCase() + "=")) { + parseLibraryName(builder, queryParam); + } + + if (forStartWith.startsWith(PARAMETER_NAME_LIBRARY_VERSION.toLowerCase() + "=")) { + parseLibraryVersion(builder, queryParam); + } + if (forStartWith.startsWith(PARAMETER_NAME_VERIFY_PEER.toLowerCase() + "=")) { parseVerifyPeer(builder, queryParam); } @@ -897,6 +973,14 @@ private String getQueryString() { queryPairs.add(PARAMETER_NAME_CLIENT_NAME + "=" + urlEncode(clientName)); } + if (libraryName != null && !libraryName.equals(LettuceVersion.getName())) { + queryPairs.add(PARAMETER_NAME_LIBRARY_NAME + "=" + urlEncode(libraryName)); + } + + if (libraryVersion != null && !libraryVersion.equals(LettuceVersion.getVersion())) { + queryPairs.add(PARAMETER_NAME_LIBRARY_VERSION + "=" + urlEncode(libraryVersion)); + } + if (isSsl() && getVerifyMode() != SslVerifyMode.FULL) { queryPairs.add(PARAMETER_NAME_VERIFY_PEER + "=" + verifyMode.name()); } @@ -914,7 +998,7 @@ private String getQueryString() { } } - return queryPairs.stream().collect(Collectors.joining("&")); + return String.join("&", queryPairs); } private String getPortPart(int port, String scheme) { @@ -1073,6 +1157,22 @@ private static void parseClientName(Builder builder, String queryParam) { } } + private static void parseLibraryName(Builder builder, String queryParam) { + + String libraryName = getValuePart(queryParam); + if (isNotEmpty(libraryName)) { + builder.withLibraryName(libraryName); + } + } + + private static void parseLibraryVersion(Builder builder, String queryParam) { + + String libraryVersion = getValuePart(queryParam); + if (isNotEmpty(libraryVersion)) { + builder.withLibraryVersion(libraryVersion); + } + } + private static void parseVerifyPeer(Builder builder, String queryParam) { String verifyPeer = getValuePart(queryParam); @@ -1217,6 +1317,10 @@ public static class Builder { private String clientName; + private String libraryName = LettuceVersion.getName(); + + private String libraryVersion = LettuceVersion.getVersion(); + private String username; private char[] password; @@ -1537,7 +1641,7 @@ public Builder withDatabase(int database) { } /** - * Configures a client name. + * Configures a client name. Sets client name also for already configured Redis Sentinel nodes. * * @param clientName the client name * @return the builder @@ -1547,6 +1651,47 @@ public Builder withClientName(String clientName) { LettuceAssert.notNull(clientName, "Client name must not be null"); this.clientName = clientName; + this.sentinels.forEach(it -> it.setClientName(clientName)); + return this; + } + + /** + * Configures a library name. Sets library name also for already configured Redis Sentinel nodes. + * + * @param libraryName the library name + * @return the builder + * @since 6.3 + */ + public Builder withLibraryName(String libraryName) { + + LettuceAssert.notNull(libraryName, "Library name must not be null"); + + if (libraryName.indexOf(' ') != -1) { + throw new IllegalArgumentException("Library name must not contain spaces"); + } + + this.libraryName = libraryName; + this.sentinels.forEach(it -> it.setLibraryName(libraryName)); + return this; + } + + /** + * Configures a library version. Sets library version also for already configured Redis Sentinel nodes. + * + * @param libraryVersion the library version + * @return the builder + * @since 6.3 + */ + public Builder withLibraryVersion(String libraryVersion) { + + LettuceAssert.notNull(libraryVersion, "Library version must not be null"); + + if (libraryVersion.indexOf(' ') != -1) { + throw new IllegalArgumentException("Library version must not contain spaces"); + } + + this.libraryVersion = libraryVersion; + this.sentinels.forEach(it -> it.setLibraryVersion(libraryVersion)); return this; } @@ -1725,6 +1870,8 @@ public RedisURI build() { redisURI.setDatabase(database); redisURI.setClientName(clientName); + redisURI.setLibraryName(libraryName); + redisURI.setLibraryVersion(libraryVersion); redisURI.setSentinelMasterId(sentinelMasterId); diff --git a/src/test/java/io/lettuce/core/AuthenticationIntegrationTests.java b/src/test/java/io/lettuce/core/AuthenticationIntegrationTests.java index dda2fc3325..acbf686cc0 100644 --- a/src/test/java/io/lettuce/core/AuthenticationIntegrationTests.java +++ b/src/test/java/io/lettuce/core/AuthenticationIntegrationTests.java @@ -56,7 +56,7 @@ void setUp(StatefulRedisConnection connection) { void authAsJohn(RedisClient client) { RedisURI uri = RedisURI.builder().withHost(TestSettings.host()).withPort(TestSettings.port()) - .withAuthentication("john", "foobared").build(); + .withAuthentication("john", "foobared").withLibraryName("").withLibraryVersion("").build(); StatefulRedisConnection connection = client.connect(uri); diff --git a/src/test/java/io/lettuce/core/RedisURIBuilderUnitTests.java b/src/test/java/io/lettuce/core/RedisURIBuilderUnitTests.java index 774f4542b6..2a26a7b028 100644 --- a/src/test/java/io/lettuce/core/RedisURIBuilderUnitTests.java +++ b/src/test/java/io/lettuce/core/RedisURIBuilderUnitTests.java @@ -84,6 +84,14 @@ void redisWithClientName() { assertThat(result.getClientName()).isEqualTo("hello"); } + @Test + void redisWithLibrary() { + RedisURI result = RedisURI.Builder.redis("localhost").withLibraryName("name").withLibraryVersion("1.foo").build(); + + assertThat(result.getLibraryName()).isEqualTo("name"); + assertThat(result.getLibraryVersion()).isEqualTo("1.foo"); + } + @Test void redisHostAndPortWithInvalidPort() { assertThatThrownBy(() -> RedisURI.Builder.redis("localhost", -1)).isInstanceOf(IllegalArgumentException.class); @@ -333,6 +341,8 @@ void shouldInitializeBuilder() { source.setPort(1234); source.setTimeout(Duration.ofSeconds(2)); source.setClientName("foo"); + source.setLibraryName("lib"); + source.setLibraryVersion("libver"); source.setUsername("foo"); source.setPassword("bar"); source.setDatabase(4); @@ -350,6 +360,8 @@ void shouldInitializeBuilder() { assertThat(target.getPassword()).isEqualTo("bar".toCharArray()); assertThat(target.getTimeout()).isEqualTo(source.getTimeout()); assertThat(target.getClientName()).isEqualTo(source.getClientName()); + assertThat(target.getLibraryName()).isEqualTo(source.getLibraryName()); + assertThat(target.getLibraryVersion()).isEqualTo(source.getLibraryVersion()); assertThat(target.getSocket()).isEqualTo(source.getSocket()); assertThat(target.getDatabase()).isEqualTo(source.getDatabase()); assertThat(target.isStartTls()).isEqualTo(source.isStartTls()); diff --git a/src/test/java/io/lettuce/core/RedisURIUnitTests.java b/src/test/java/io/lettuce/core/RedisURIUnitTests.java index 3063a7987e..50b0e7e378 100644 --- a/src/test/java/io/lettuce/core/RedisURIUnitTests.java +++ b/src/test/java/io/lettuce/core/RedisURIUnitTests.java @@ -273,6 +273,15 @@ void clientNameParsingTest() { assertThat(redisURI).hasToString("redis://****@localhost:1234?clientName=hello"); } + @Test + void libraryParsingTest() { + RedisURI redisURI = RedisURI.create("redis://auth@localhost:1234/?libraryName=lib&libraryVersion=1.0"); + assertThat(redisURI.getLibraryName()).isEqualTo("lib"); + assertThat(redisURI.getLibraryVersion()).isEqualTo("1.0"); + + assertThat(redisURI).hasToString("redis://****@localhost:1234?libraryName=lib&libraryVersion=1.0"); + } + @Test void parsingWithInvalidValuesTest() { RedisURI redisURI = RedisURI diff --git a/src/test/java/io/lettuce/core/cluster/ClusterClientOptionsIntegrationTests.java b/src/test/java/io/lettuce/core/cluster/ClusterClientOptionsIntegrationTests.java index 04e6098b0b..fda825d310 100644 --- a/src/test/java/io/lettuce/core/cluster/ClusterClientOptionsIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cluster/ClusterClientOptionsIntegrationTests.java @@ -15,7 +15,7 @@ */ package io.lettuce.core.cluster; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.*; import java.time.Duration; import java.util.concurrent.ExecutionException; diff --git a/src/test/java/io/lettuce/core/cluster/NodeSelectionSyncIntegrationTests.java b/src/test/java/io/lettuce/core/cluster/NodeSelectionSyncIntegrationTests.java index 33c2552088..cad78886fd 100644 --- a/src/test/java/io/lettuce/core/cluster/NodeSelectionSyncIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cluster/NodeSelectionSyncIntegrationTests.java @@ -50,6 +50,7 @@ class NodeSelectionSyncIntegrationTests extends TestSupport { private final RedisClusterClient clusterClient; + private final RedisAdvancedClusterCommands commands; @Inject @@ -229,4 +230,5 @@ static void waitForReplication(RedisAdvancedClusterCommands comm return null; }).waitOrTimeout(); } + } diff --git a/src/test/java/io/lettuce/core/sentinel/SentinelServerCommandIntegrationTests.java b/src/test/java/io/lettuce/core/sentinel/SentinelServerCommandIntegrationTests.java index f3841b29d5..64f45dda85 100644 --- a/src/test/java/io/lettuce/core/sentinel/SentinelServerCommandIntegrationTests.java +++ b/src/test/java/io/lettuce/core/sentinel/SentinelServerCommandIntegrationTests.java @@ -27,7 +27,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import io.lettuce.core.ClientListArgs; import io.lettuce.core.KillArgs; import io.lettuce.core.RedisClient; import io.lettuce.core.RedisURI; @@ -123,27 +122,10 @@ public void clientList() { assertThat(sentinel.clientList().contains("addr=")).isTrue(); } - @Test - public void clientListExtended() { - assertThat(sentinel.clientList(ClientListArgs.Builder.typeMaster()).contains("addr=")).isTrue(); - assertThat(sentinel.clientList(ClientListArgs.Builder.typeSlave()).contains("addr=")).isFalse(); - } - @Test public void info() { assertThat(sentinel.info().contains("redis_version")).isTrue(); assertThat(sentinel.info("server").contains("redis_version")).isTrue(); } - @Test - public void clientInfo() { - assertThat(sentinel.clientInfo().contains("addr=")).isTrue(); - } - - @Test - public void clientSetinfo() { - sentinel.clientSetinfo("lib-name", "lettuce"); - - assertThat(sentinel.clientInfo().contains("lib-name=lettuce")).isTrue(); - } } diff --git a/src/test/java/io/lettuce/core/tracing/BraveTracingIntegrationTests.java b/src/test/java/io/lettuce/core/tracing/BraveTracingIntegrationTests.java index 197e2ae429..4331037221 100644 --- a/src/test/java/io/lettuce/core/tracing/BraveTracingIntegrationTests.java +++ b/src/test/java/io/lettuce/core/tracing/BraveTracingIntegrationTests.java @@ -28,8 +28,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import reactor.test.StepVerifier; -import zipkin2.Span; import brave.ScopedSpan; import brave.Tracer; import brave.Tracing; @@ -44,6 +42,8 @@ import io.lettuce.test.Wait; import io.lettuce.test.condition.EnabledOnCommand; import io.lettuce.test.resource.FastShutdown; +import reactor.test.StepVerifier; +import zipkin2.Span; /** * Integration tests for {@link BraveTracing}. @@ -56,8 +56,11 @@ class BraveTracingIntegrationTests extends TestSupport { private static ClientResources clientResources; + private static RedisClient client; + private static Tracing clientTracing; + private static Queue spans = new LinkedBlockingQueue<>(); @BeforeAll @@ -67,7 +70,8 @@ static void beforeClass() { .currentTraceContext(CurrentTraceContext.Default.create()).spanReporter(spans::add).build(); clientResources = DefaultClientResources.builder().tracing(BraveTracing.create(clientTracing)).build(); - client = RedisClient.create(clientResources, RedisURI.Builder.redis(host, port).build()); + client = RedisClient.create(clientResources, + RedisURI.Builder.redis(host, port).withLibraryVersion("").withLibraryName("").build()); } @BeforeEach @@ -137,7 +141,8 @@ void getAndSetWithTraceWithCommandArgsExcludedFromTags() { ClientResources clientResources = ClientResources.builder() .tracing(BraveTracing.builder().tracing(clientTracing).excludeCommandArgsFromSpanTags().build()).build(); - RedisClient client = RedisClient.create(clientResources, RedisURI.Builder.redis(host, port).build()); + RedisClient client = RedisClient.create(clientResources, + RedisURI.Builder.redis(host, port).withLibraryName("").withLibraryVersion("").build()); ScopedSpan trace = clientTracing.tracer().startScopedSpan("foo"); @@ -224,7 +229,8 @@ void reactiveGetAndSetWithTraceProvider() { StatefulRedisConnection connect = client.connect(); connect.reactive().set("foo", "bar").then(connect.reactive().get("foo")) - .contextWrite(io.lettuce.core.tracing.Tracing.withTraceContextProvider(() -> BraveTracing.BraveTraceContext.create(trace.context()))) // + .contextWrite(io.lettuce.core.tracing.Tracing + .withTraceContextProvider(() -> BraveTracing.BraveTraceContext.create(trace.context()))) // .as(StepVerifier::create) // .expectNext("bar").verifyComplete(); @@ -237,4 +243,5 @@ void reactiveGetAndSetWithTraceProvider() { assertThat(spans.get(1).name()).isEqualTo("set"); assertThat(spans.get(2).name()).isEqualTo("get"); } + }