diff --git a/docs/src/content/docs/valkey/drivers.md b/docs/src/content/docs/valkey/drivers.md index b5aa29ef..c4db4d2f 100644 --- a/docs/src/content/docs/valkey/drivers.md +++ b/docs/src/content/docs/valkey/drivers.md @@ -73,7 +73,7 @@ The following overview explains features that are supported by the individual Va io.valkey valkey-glide - 2.4.0 + ${version} ``` @@ -136,7 +136,7 @@ For more detailed client configuration options, see `io.valkey.springframework.d io.lettuce lettuce-core - 6.3.2.RELEASE + ${version} @@ -212,7 +212,7 @@ Netty currently supports the epoll (Linux) and kqueue (BSD/macOS) interfaces for valkey.clients jedis - 5.1.2 + ${version} diff --git a/pom.xml b/pom.xml index f099cf44..ebc6e885 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ 4.1.0 - 2.4.2 + 2.5.2 7.5.2.RELEASE 7.4.1 2.12.0 diff --git a/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ClusterGlideClientAdapter.java b/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ClusterGlideClientAdapter.java index 223be4d1..cbc24a2a 100644 --- a/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ClusterGlideClientAdapter.java +++ b/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ClusterGlideClientAdapter.java @@ -175,8 +175,8 @@ else if (!password.equals(ValkeyPassword.none())) { subConfigBuilder.callback((msg, context) -> this.listener.onMessage(msg, context)); configBuilder.subscriptionConfiguration(subConfigBuilder.build()); - // Set library name for server-side client identification - configBuilder.libName("GlideSpringDataValkey"); + // Report as GlideJava(SpringDataValkey), preserving the underlying driver identity. + configBuilder.clientInfoTag(CLIENT_INFO_TAG); // Build and create cluster client GlideClusterClientConfiguration config = configBuilder.build(); @@ -415,8 +415,11 @@ public Object customCommand(GlideString[] args) throws InterruptedException, Exe ClusterValue clusterValue = nextCommandRoute == null ? glideClusterClient.customCommand(args).get() : glideClusterClient.customCommand(args, nextCommandRoute).get(); - // Case 1: Explicit multi-node route - return multiValue for aggregation - if (needToAggregateResult(nextCommandRoute)) { + // Case 1: Explicit multi-node route - return multiValue for aggregation. + // Guard with hasMultiData(): some commands routed to all primaries (e.g. SAVE) + // return a single value rather than a per-node map, in which case we fall + // through to single-value handling instead of failing. + if (needToAggregateResult(nextCommandRoute) && clusterValue.hasMultiData()) { return clusterValue.getMultiValue(); } diff --git a/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/StandaloneGlideClientAdapter.java b/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/StandaloneGlideClientAdapter.java index 37bb5928..78a54e2c 100644 --- a/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/StandaloneGlideClientAdapter.java +++ b/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/StandaloneGlideClientAdapter.java @@ -163,8 +163,8 @@ else if (!password.equals(ValkeyPassword.none())) { subConfigBuilder.callback((msg, context) -> this.listener.onMessage(msg, context)); configBuilder.subscriptionConfiguration(subConfigBuilder.build()); - // Set library name for server-side client identification - configBuilder.libName("GlideSpringDataValkey"); + // Report as GlideJava(SpringDataValkey), preserving the underlying driver identity. + configBuilder.clientInfoTag(CLIENT_INFO_TAG); // Build and create client GlideClientConfiguration config = configBuilder.build(); diff --git a/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/UnifiedGlideClient.java b/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/UnifiedGlideClient.java index 58e73f28..50661f18 100644 --- a/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/UnifiedGlideClient.java +++ b/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/UnifiedGlideClient.java @@ -29,6 +29,12 @@ */ interface UnifiedGlideClient extends AutoCloseable { + /** + * Client information tag reported to the server via GLIDE's {@code clientInfoTag}, producing a + * lib-name of {@code GlideJava(SpringDataValkey)} for server-side identification. + */ + String CLIENT_INFO_TAG = "SpringDataValkey"; + public enum BatchStatus { None, Pipeline, Transaction, diff --git a/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ValkeyGlideClusterServerCommands.java b/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ValkeyGlideClusterServerCommands.java index 53f9c1c7..fa366138 100644 --- a/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ValkeyGlideClusterServerCommands.java +++ b/spring-data-valkey/src/main/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ValkeyGlideClusterServerCommands.java @@ -76,9 +76,12 @@ public Long lastSave() { (Map rawResult) -> rawResult.isEmpty() ? null : Collections.max(rawResult.values())); } + @Override public void save() { - // valkey-glide default route is "random", so we specify ALL_PRIMARIES - connection.execute(SimpleMultiNodeRoute.ALL_PRIMARIES, "SAVE", (Map rawResult) -> null); + // valkey-glide default route is "random", so we specify ALL_PRIMARIES. + // GLIDE 2.5.x aggregates SAVE to a single value (rather than a per-node map); + // the response is discarded, so accept Object. + connection.execute(SimpleMultiNodeRoute.ALL_PRIMARIES, "SAVE", (Object rawResult) -> null); } @Override diff --git a/spring-data-valkey/src/test/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ValkeyGlideConnectionClusterServerCommandsIntegrationTests.java b/spring-data-valkey/src/test/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ValkeyGlideConnectionClusterServerCommandsIntegrationTests.java index 77a9e375..f805a608 100644 --- a/spring-data-valkey/src/test/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ValkeyGlideConnectionClusterServerCommandsIntegrationTests.java +++ b/spring-data-valkey/src/test/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ValkeyGlideConnectionClusterServerCommandsIntegrationTests.java @@ -478,7 +478,8 @@ void testGetClientListOnSpecificNode() { @EnabledOnValkeyVersion("7.2") void testClientLibNameReported() { List clientList = clusterConnection.serverCommands().getClientList(); - assertThat(clientList).anyMatch(client -> "GlideSpringDataValkey".equals(client.get("lib-name"))); + assertThat(clientList).anyMatch( + client -> client.get("lib-name") != null && client.get("lib-name").contains("(SpringDataValkey)")); } // ==================== Cluster-Wide Routing and Aggregation Verification diff --git a/spring-data-valkey/src/test/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ValkeyGlideConnectionServerCommandsIntegrationTests.java b/spring-data-valkey/src/test/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ValkeyGlideConnectionServerCommandsIntegrationTests.java index 26693718..6de5cb91 100644 --- a/spring-data-valkey/src/test/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ValkeyGlideConnectionServerCommandsIntegrationTests.java +++ b/spring-data-valkey/src/test/java/io/valkey/springframework/data/valkey/connection/valkeyglide/ValkeyGlideConnectionServerCommandsIntegrationTests.java @@ -481,7 +481,8 @@ void testGetClientList() { @EnabledOnValkeyVersion("7.2") void testClientLibNameReported() { List clientList = connection.serverCommands().getClientList(); - assertThat(clientList).anyMatch(client -> "GlideSpringDataValkey".equals(client.get("lib-name"))); + assertThat(clientList).anyMatch( + client -> client.get("lib-name") != null && client.get("lib-name").contains("(SpringDataValkey)")); } @Test