-
Notifications
You must be signed in to change notification settings - Fork 0
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
Add cluster client, request routes configuration and support for bulk response #59
Changes from all commits
2639c41
05b87d3
4d2b723
7687a66
62cf838
5807737
19d9d9f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package glide.api; | ||
|
||
import static glide.api.RedisClient.buildChannelHandler; | ||
import static glide.api.RedisClient.buildCommandManager; | ||
import static glide.api.RedisClient.buildConnectionManager; | ||
|
||
import glide.api.commands.ClusterBaseCommands; | ||
import glide.api.models.ClusterValue; | ||
import glide.api.models.configuration.RedisClusterClientConfiguration; | ||
import glide.api.models.configuration.Route; | ||
import glide.connectors.handlers.ChannelHandler; | ||
import glide.managers.CommandManager; | ||
import glide.managers.ConnectionManager; | ||
import glide.managers.models.Command; | ||
import java.util.Optional; | ||
import java.util.concurrent.CompletableFuture; | ||
|
||
/** | ||
* Async (non-blocking) client for Redis in Cluster mode. Use {@link #CreateClient} to request a | ||
* client to Redis. | ||
*/ | ||
public class RedisClusterClient extends BaseClient implements ClusterBaseCommands { | ||
|
||
protected RedisClusterClient(ConnectionManager connectionManager, CommandManager commandManager) { | ||
super(connectionManager, commandManager); | ||
} | ||
|
||
/** | ||
* Async request for an async (non-blocking) Redis client in Cluster mode. | ||
* | ||
* @param config Redis cluster client Configuration | ||
* @return a Future to connect and return a ClusterClient | ||
*/ | ||
public static CompletableFuture<RedisClusterClient> CreateClient( | ||
RedisClusterClientConfiguration config) { | ||
try { | ||
ChannelHandler channelHandler = buildChannelHandler(); | ||
ConnectionManager connectionManager = buildConnectionManager(channelHandler); | ||
CommandManager commandManager = buildCommandManager(channelHandler); | ||
// TODO: Support exception throwing, including interrupted exceptions | ||
return connectionManager | ||
.connectToRedis(config) | ||
.thenApply(ignored -> new RedisClusterClient(connectionManager, commandManager)); | ||
} catch (InterruptedException e) { | ||
// Something bad happened while we were establishing netty connection to UDS | ||
var future = new CompletableFuture<RedisClusterClient>(); | ||
future.completeExceptionally(e); | ||
return future; | ||
} | ||
} | ||
|
||
@Override | ||
public CompletableFuture<ClusterValue<Object>> customCommand(String[] args) { | ||
Command command = | ||
Command.builder().requestType(Command.RequestType.CUSTOM_COMMAND).arguments(args).build(); | ||
return commandManager.submitNewCommand( | ||
command, Optional.empty(), response -> ClusterValue.of(handleObjectResponse(response))); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<ClusterValue<Object>> customCommand(String[] args, Route route) { | ||
Command command = | ||
Command.builder().requestType(Command.RequestType.CUSTOM_COMMAND).arguments(args).build(); | ||
return commandManager.submitNewCommand( | ||
command, Optional.of(route), response -> ClusterValue.of(handleObjectResponse(response))); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package glide.api.commands; | ||
|
||
import glide.api.models.ClusterValue; | ||
import glide.api.models.configuration.Route; | ||
import java.util.concurrent.CompletableFuture; | ||
|
||
/** | ||
* Base Commands interface to handle generic command and transaction requests with routing options. | ||
*/ | ||
public interface ClusterBaseCommands { | ||
|
||
/** | ||
* Executes a single command, without checking inputs. Every part of the command, including | ||
* subcommands, should be added as a separate value in {@code args}. | ||
* | ||
* @remarks This function should only be used for single-response commands. Commands that don't | ||
* return response (such as <em>SUBSCRIBE</em>), or that return potentially more than a single | ||
* response (such as <em>XREAD</em>), or that change the client's behavior (such as entering | ||
* <em>pub</em>/<em>sub</em> mode on <em>RESP2</em> connections) shouldn't be called using | ||
* this function. | ||
* @example Returns a list of all <em>pub</em>/<em>sub</em> clients: | ||
* <p><code> | ||
* Object result = client.customCommand(new String[]{ "CLIENT", "LIST", "TYPE", "PUBSUB" }).get(); | ||
* </code> | ||
* @param args Arguments for the custom command including the command name | ||
* @return A <em>CompletableFuture</em> with response result from Redis | ||
*/ | ||
CompletableFuture<ClusterValue<Object>> customCommand(String[] args); | ||
|
||
/** | ||
* Executes a single command, without checking inputs. Every part of the command, including | ||
* subcommands, should be added as a separate value in {@code args}. | ||
* | ||
* @remarks This function should only be used for single-response commands. Commands that don't | ||
* return response (such as <em>SUBSCRIBE</em>), or that return potentially more than a single | ||
* response (such as <em>XREAD</em>), or that change the client's behavior (such as entering | ||
* <em>pub</em>/<em>sub</em> mode on <em>RESP2</em> connections) shouldn't be called using | ||
* this function. | ||
* @example Returns a list of all <em>pub</em>/<em>sub</em> clients: | ||
* <p><code> | ||
* Object result = client.customCommand(new String[]{ "CLIENT", "LIST", "TYPE", "PUBSUB" }).get(); | ||
* </code> | ||
* @param args Arguments for the custom command including the command name | ||
* @param route Routing configuration for the command | ||
* @return A <em>CompletableFuture</em> with response result from Redis | ||
*/ | ||
CompletableFuture<ClusterValue<Object>> customCommand(String[] args, Route route); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package glide.api.models; | ||
|
||
import java.util.Map; | ||
|
||
/** | ||
* union-like type which can store single-value or multi-value retrieved from Redis. The | ||
* multi-value, if defined, contains the routed value as a Map<String, Object> containing a cluster | ||
* node address to cluster node value. | ||
* | ||
* @param <T> The wrapped data type | ||
*/ | ||
public class ClusterValue<T> { | ||
private Map<String, T> multiValue = null; | ||
|
||
private T singleValue = null; | ||
|
||
private ClusterValue() {} | ||
|
||
/** | ||
* Get per-node value.<br> | ||
* Check with {@link #hasMultiData()} prior to accessing the data. | ||
*/ | ||
public Map<String, T> getMultiValue() { | ||
assert hasMultiData(); | ||
return multiValue; | ||
} | ||
|
||
/** | ||
* Get the single value.<br> | ||
* Check with {@link #hasSingleData()} ()} prior to accessing the data. | ||
*/ | ||
public T getSingleValue() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. javadoc - please mention that hasSingleValue() should be called first to avoid exceptions |
||
assert hasSingleData(); | ||
return singleValue; | ||
} | ||
|
||
/** A constructor for the value. */ | ||
@SuppressWarnings("unchecked") | ||
public static <T> ClusterValue<T> of(Object data) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. javadoc |
||
var res = new ClusterValue<T>(); | ||
if (data instanceof Map) { | ||
res.multiValue = (Map<String, T>) data; | ||
} else { | ||
res.singleValue = (T) data; | ||
} | ||
return res; | ||
} | ||
|
||
/** Check that multi-value is stored in this object. Use it prior to accessing the data. */ | ||
public boolean hasMultiData() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. javadoc - please be more specific since this is exposed to the user. |
||
return multiValue != null; | ||
} | ||
|
||
/** Check that single-value is stored in this object. Use it prior to accessing the data. */ | ||
public boolean hasSingleData() { | ||
return !hasMultiData(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not opposed, but why not
static
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no idea