Skip to content

Commit

Permalink
Java: Add INFO() commands to java client (valkey-io#920)
Browse files Browse the repository at this point in the history
* Update commandmanager to remove optional<route> argument

Signed-off-by: Andrew Carbonetto <[email protected]>

* Java: Add Info commands to client

Signed-off-by: Andrew Carbonetto <[email protected]>

* Clean tests

Signed-off-by: Andrew Carbonetto <[email protected]>

* Fix build.gradle

Signed-off-by: Andrew Carbonetto <[email protected]>

* Update to merge easier

Signed-off-by: Andrew Carbonetto <[email protected]>

* Spotless

Signed-off-by: Andrew Carbonetto <[email protected]>

* Update javadoc for info clustered

Signed-off-by: Andrew Carbonetto <[email protected]>

---------

Signed-off-by: Andrew Carbonetto <[email protected]>
  • Loading branch information
acarbonetto authored Feb 12, 2024
1 parent 47bc798 commit e6a0de5
Show file tree
Hide file tree
Showing 12 changed files with 545 additions and 8 deletions.
4 changes: 4 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ private <T> T handleRedisResponse(Class<T> classType, boolean isNullable, Respon
+ classType.getSimpleName());
}

protected Object handleObjectResponse(Response response) throws RedisException {
return handleRedisResponse(Object.class, false, response);
}

protected Object handleObjectOrNullResponse(Response response) throws RedisException {
return handleRedisResponse(Object.class, true, response);
}
Expand Down
15 changes: 14 additions & 1 deletion java/client/src/main/java/glide/api/RedisClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
package glide.api;

import static redis_request.RedisRequestOuterClass.RequestType.CustomCommand;
import static redis_request.RedisRequestOuterClass.RequestType.Info;

import glide.api.commands.GenericCommands;
import glide.api.commands.ServerManagementCommands;
import glide.api.models.commands.InfoOptions;
import glide.api.models.configuration.RedisClientConfiguration;
import glide.managers.CommandManager;
import glide.managers.ConnectionManager;
Expand All @@ -14,7 +17,7 @@
* Async (non-blocking) client for Redis in Standalone mode. Use {@link #CreateClient} to request a
* client to Redis.
*/
public class RedisClient extends BaseClient implements GenericCommands {
public class RedisClient extends BaseClient implements GenericCommands, ServerManagementCommands {

protected RedisClient(ConnectionManager connectionManager, CommandManager commandManager) {
super(connectionManager, commandManager);
Expand All @@ -34,4 +37,14 @@ public static CompletableFuture<RedisClient> CreateClient(RedisClientConfigurati
public CompletableFuture<Object> customCommand(@NonNull String[] args) {
return commandManager.submitNewCommand(CustomCommand, args, this::handleObjectOrNullResponse);
}

@Override
public CompletableFuture<String> info() {
return commandManager.submitNewCommand(Info, new String[0], this::handleStringResponse);
}

@Override
public CompletableFuture<String> info(@NonNull InfoOptions options) {
return commandManager.submitNewCommand(Info, options.toArgs(), this::handleStringResponse);
}
}
32 changes: 31 additions & 1 deletion java/client/src/main/java/glide/api/RedisClusterClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
package glide.api;

import static redis_request.RedisRequestOuterClass.RequestType.CustomCommand;
import static redis_request.RedisRequestOuterClass.RequestType.Info;
import static redis_request.RedisRequestOuterClass.RequestType.Ping;

import glide.api.commands.ConnectionManagementClusterCommands;
import glide.api.commands.GenericClusterCommands;
import glide.api.commands.ServerManagementClusterCommands;
import glide.api.models.ClusterValue;
import glide.api.models.commands.InfoOptions;
import glide.api.models.configuration.RedisClusterClientConfiguration;
import glide.api.models.configuration.RequestRoutingConfiguration.Route;
import glide.managers.CommandManager;
Expand All @@ -20,7 +23,9 @@
* client to Redis.
*/
public class RedisClusterClient extends BaseClient
implements ConnectionManagementClusterCommands, GenericClusterCommands {
implements ConnectionManagementClusterCommands,
GenericClusterCommands,
ServerManagementClusterCommands {

protected RedisClusterClient(ConnectionManager connectionManager, CommandManager commandManager) {
super(connectionManager, commandManager);
Expand Down Expand Up @@ -68,4 +73,29 @@ public CompletableFuture<String> ping(@NonNull String str, @NonNull Route route)
return commandManager.submitNewCommand(
Ping, new String[] {str}, route, this::handleStringResponse);
}

@Override
public CompletableFuture<ClusterValue<String>> info() {
return commandManager.submitNewCommand(
Info, new String[0], response -> ClusterValue.of(handleObjectResponse(response)));
}

@Override
public CompletableFuture<ClusterValue<String>> info(@NonNull Route route) {
return commandManager.submitNewCommand(
Info, new String[0], route, response -> ClusterValue.of(handleObjectResponse(response)));
}

@Override
public CompletableFuture<ClusterValue<String>> info(@NonNull InfoOptions options) {
return commandManager.submitNewCommand(
Info, options.toArgs(), response -> ClusterValue.of(handleObjectResponse(response)));
}

@Override
public CompletableFuture<ClusterValue<String>> info(
@NonNull InfoOptions options, @NonNull Route route) {
return commandManager.submitNewCommand(
Info, options.toArgs(), route, response -> ClusterValue.of(handleObjectResponse(response)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.commands;

import glide.api.models.ClusterValue;
import glide.api.models.commands.InfoOptions;
import glide.api.models.configuration.RequestRoutingConfiguration.Route;
import java.util.concurrent.CompletableFuture;

/**
* Server Management Commands interface.
*
* @see <a href="https://redis.io/commands/?group=server">Server Management Commands</a>
*/
public interface ServerManagementClusterCommands {

/**
* Get information and statistics about the Redis server. DEFAULT option is assumed. The command
* will be routed to all primaries.
*
* @see <a href="https://redis.io/commands/info/">redis.io</a> for details. {@link
* InfoOptions.Section#DEFAULT} option is assumed.
* @return Response from Redis cluster with a <code>Map{@literal <String, String>}</code> with
* each address as the key and its corresponding value is the information for the node.
* @example
* <p><code>
* {@literal Map<String, String>} routedInfoResult = clusterClient.info().get().getMultiValue();
* </code>
*/
CompletableFuture<ClusterValue<String>> info();

/**
* Get information and statistics about the Redis server. DEFAULT option is assumed
*
* @see <a href="https://redis.io/commands/info/">redis.io</a> for details.
* @param route Routing configuration for the command. Client will route the command to the nodes
* defined.
* @return Response from Redis cluster with a <code>String</code> with the requested Sections.
* When specifying a <code>route</code> other than a single node, it returns a <code>
* Map{@literal <String, String>}</code> with each address as the key and its corresponding
* value is the information for the node.
*/
CompletableFuture<ClusterValue<String>> info(Route route);

/**
* Get information and statistics about the Redis server. The command will be routed to all
* primaries.
*
* @see <a href="https://redis.io/commands/info/">redis.io</a> for details.
* @param options - A list of {@link InfoOptions.Section} values specifying which sections of
* information to retrieve. When no parameter is provided, the {@link
* InfoOptions.Section#DEFAULT} option is assumed.
* @return Response from Redis cluster with a <code>Map{@literal <String, String>}</code> with
* each address as the key and its corresponding value is the information of the sections
* requested for the node.
*/
CompletableFuture<ClusterValue<String>> info(InfoOptions options);

/**
* Get information and statistics about the Redis server.
*
* @see <a href="https://redis.io/commands/info/">redis.io</a> for details.
* @param options - A list of {@link InfoOptions.Section} values specifying which sections of
* information to retrieve. When no parameter is provided, the {@link
* InfoOptions.Section#DEFAULT} option is assumed.
* @param route Routing configuration for the command. Client will route the command to the nodes
* defined.
* @return Response from Redis cluster with a <code>String</code> with the requested sections.
* When specifying a <code>route</code> other than a single node, it returns a <code>
* Map{@literal <String, String>}</code> with each address as the key and its corresponding
* value is the information of the sections requested for the node.
*/
CompletableFuture<ClusterValue<String>> info(InfoOptions options, Route route);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.commands;

import glide.api.models.commands.InfoOptions;
import glide.api.models.commands.InfoOptions.Section;
import java.util.concurrent.CompletableFuture;

/**
* Server Management Commands interface.
*
* @see <a href="https://redis.io/commands/?group=server">Server Management Commands</a>
*/
public interface ServerManagementCommands {

/**
* Get information and statistics about the Redis server. No argument is provided, so the {@link
* Section#DEFAULT} option is assumed.
*
* @see <a href="https://redis.io/commands/info/">redis.io</a> for details.
* @return Response from Redis containing a <code>String</code> with the information for the
* default sections.
*/
CompletableFuture<String> info();

/**
* Get information and statistics about the Redis server.
*
* @see <a href="https://redis.io/commands/info/">redis.io</a> for details.
* @param options A list of {@link Section} values specifying which sections of information to
* retrieve. When no parameter is provided, the {@link Section#DEFAULT} option is assumed.
* @return Response from Redis containing a <code>String</code> with the information for the
* sections requested.
*/
CompletableFuture<String> info(InfoOptions options);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.models.commands;

import glide.api.commands.ServerManagementCommands;
import java.util.List;
import lombok.Builder;
import lombok.Singular;

/**
* Optional arguments to {@link ServerManagementCommands#info(InfoOptions)}
*
* @see <a href="https://redis.io/commands/info/">redis.io</a>
*/
@Builder
public final class InfoOptions {

@Singular private final List<Section> sections;

public enum Section {
/** SERVER: General information about the Redis server */
SERVER,
/** CLIENTS: Client connections section */
CLIENTS,
/** MEMORY: Memory consumption related information */
MEMORY,
/** PERSISTENCE: RDB and AOF related information */
PERSISTENCE,
/** STATS: General statistics */
STATS,
/** REPLICATION: Master/replica replication information */
REPLICATION,
/** CPU: CPU consumption statistics */
CPU,
/** COMMANDSTATS: Redis command statistics */
COMMANDSTATS,
/** LATENCYSTATS: Redis command latency percentile distribution statistics */
LATENCYSTATS,
/** SENTINEL: Redis Sentinel section (only applicable to Sentinel instances) */
SENTINEL,
/** CLUSTER: Redis Cluster section */
CLUSTER,
/** MODULES: Modules section */
MODULES,
/** KEYSPACE: Database related statistics */
KEYSPACE,
/** ERRORSTATS: Redis error statistics */
ERRORSTATS,
/** ALL: Return all sections (excluding module generated ones) */
ALL,
/** DEFAULT: Return only the default set of sections */
DEFAULT,
/** EVERYTHING: Includes all and modules */
EVERYTHING,
}

/**
* Converts options enum into a String[] to add to a Redis request.
*
* @return String[]
*/
public String[] toArgs() {
return sections.stream().map(Object::toString).toArray(String[]::new);
}
}
66 changes: 66 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
import static org.mockito.Mockito.when;
import static redis_request.RedisRequestOuterClass.RequestType.CustomCommand;
import static redis_request.RedisRequestOuterClass.RequestType.GetString;
import static redis_request.RedisRequestOuterClass.RequestType.Info;
import static redis_request.RedisRequestOuterClass.RequestType.Ping;
import static redis_request.RedisRequestOuterClass.RequestType.SetString;

import glide.api.models.commands.InfoOptions;
import glide.api.models.commands.SetOptions;
import glide.api.models.commands.SetOptions.Expiry;
import glide.managers.CommandManager;
Expand Down Expand Up @@ -201,4 +203,68 @@ public void set_with_SetOptions_OnlyIfDoesNotExist_returns_success() {
assertNotNull(response);
assertEquals(value, response.get());
}

@SneakyThrows
@Test
public void info_returns_success() {
// setup
CompletableFuture<String> testResponse = mock(CompletableFuture.class);
String testPayload = "Key: Value";
when(testResponse.get()).thenReturn(testPayload);
when(commandManager.<String>submitNewCommand(eq(Info), eq(new String[0]), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<String> response = service.info();
String payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(testPayload, payload);
}

@SneakyThrows
@Test
public void info_with_multiple_InfoOptions_returns_success() {
// setup
String[] arguments =
new String[] {InfoOptions.Section.ALL.toString(), InfoOptions.Section.DEFAULT.toString()};
CompletableFuture<String> testResponse = mock(CompletableFuture.class);
String testPayload = "Key: Value";
when(testResponse.get()).thenReturn(testPayload);
when(commandManager.<String>submitNewCommand(eq(Info), eq(arguments), any()))
.thenReturn(testResponse);

// exercise
InfoOptions options =
InfoOptions.builder()
.section(InfoOptions.Section.ALL)
.section(InfoOptions.Section.DEFAULT)
.build();
CompletableFuture<String> response = service.info(options);
String payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(testPayload, payload);
}

@SneakyThrows
@Test
public void info_with_empty_InfoOptions_returns_success() {
// setup
CompletableFuture<String> testResponse = mock(CompletableFuture.class);
String testPayload = "Key: Value";
when(testResponse.get()).thenReturn(testPayload);
when(commandManager.<String>submitNewCommand(eq(Info), eq(new String[0]), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<String> response = service.info(InfoOptions.builder().build());
String payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(testPayload, payload);
}
}
Loading

0 comments on commit e6a0de5

Please sign in to comment.