-
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
Support transactions #65
Changes from all commits
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 |
---|---|---|
@@ -1,15 +1,14 @@ | ||
package glide.api.commands; | ||
|
||
import glide.ffi.resolvers.RedisValueResolver; | ||
import java.util.List; | ||
import java.util.concurrent.CompletableFuture; | ||
import response.ResponseOuterClass.Response; | ||
|
||
/** Base Commands interface to handle generic command and transaction requests. */ | ||
public interface BaseCommands { | ||
|
||
/** | ||
* default Object handler from response | ||
* The default Object handler from response | ||
* | ||
* @return BaseCommandResponseResolver to deliver the response | ||
*/ | ||
|
@@ -19,31 +18,37 @@ static BaseCommandResponseResolver applyBaseCommandResponseResolver() { | |
|
||
/** | ||
* Extracts the response from the Protobuf response and either throws an exception or returns the | ||
* appropriate response has an Object | ||
* appropriate response as an Object | ||
* | ||
* @param response Redis protobuf message | ||
* @return Response Object | ||
*/ | ||
static Object handleObjectResponse(Response response) { | ||
// return function to convert protobuf.Response into the response object by | ||
// calling valueFromPointer | ||
return BaseCommands.applyBaseCommandResponseResolver().apply(response); | ||
return applyBaseCommandResponseResolver().apply(response); | ||
} | ||
|
||
public static List<Object> handleTransactionResponse(Response response) { | ||
static Object[] handleTransactionResponse(Response response) { | ||
// return function to convert protobuf.Response into the response object by | ||
// calling valueFromPointer | ||
|
||
List<Object> transactionResponse = | ||
(List<Object>) BaseCommands.applyBaseCommandResponseResolver().apply(response); | ||
return transactionResponse; | ||
return (Object[]) applyBaseCommandResponseResolver().apply(response); | ||
} | ||
|
||
/** | ||
* Execute a @see{Command} by sending command via socket manager | ||
* Execute a custom {@link Command}. | ||
* | ||
* @param args arguments for the custom command | ||
* @return a CompletableFuture with response result from Redis | ||
*/ | ||
CompletableFuture<Object> customCommand(String[] args); | ||
|
||
/** | ||
* Execute a transaction of custom {@link Command}s. | ||
* | ||
* @param args arguments for the custom command | ||
* @return a CompletableFuture with response result from Redis | ||
*/ | ||
CompletableFuture<Object[]> customTransaction(String[][] args); | ||
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. what about exposing?
This is what the node client does: https://github.com/Bit-Quill/glide-for-redis/blob/46f831c5d2b31a667f7d7c53035b99e8b3851f26/node/src/RedisClient.ts#L98 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. We may have to create a Transactions class that implements BaseCommands that adds the transaction calls to the list. reference: https://github.com/Bit-Quill/glide-for-redis/blob/main/node/src/Transaction.ts#L72 |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,9 +3,14 @@ | |
import glide.api.commands.Command; | ||
import glide.api.commands.RedisExceptionCheckedFunction; | ||
import glide.connectors.handlers.ChannelHandler; | ||
import java.util.List; | ||
import java.util.concurrent.CompletableFuture; | ||
import lombok.RequiredArgsConstructor; | ||
import redis_request.RedisRequestOuterClass; | ||
import redis_request.RedisRequestOuterClass.Command.ArgsArray; | ||
import redis_request.RedisRequestOuterClass.RedisRequest; | ||
import redis_request.RedisRequestOuterClass.RequestType; | ||
import redis_request.RedisRequestOuterClass.Transaction; | ||
import response.ResponseOuterClass.Response; | ||
|
||
/** | ||
|
@@ -21,8 +26,8 @@ public class CommandManager { | |
/** | ||
* Build a command and send. | ||
* | ||
* @param command | ||
* @param responseHandler - to handle the response object | ||
* @param command The command to execute | ||
* @param responseHandler The handler of the response object | ||
* @return A result promise of type T | ||
*/ | ||
public <T> CompletableFuture<T> submitNewCommand( | ||
|
@@ -31,8 +36,35 @@ public <T> CompletableFuture<T> submitNewCommand( | |
// create protobuf message from command | ||
// submit async call | ||
return channel | ||
.write(prepareRedisRequest(command.getRequestType(), command.getArguments()), true) | ||
.thenApplyAsync(response -> responseHandler.apply(response)); | ||
.write( | ||
RedisRequest.newBuilder() | ||
.setSingleCommand( | ||
prepareRedisCommand(command.getRequestType(), command.getArguments())), | ||
true) | ||
.thenApplyAsync(responseHandler::apply); | ||
} | ||
|
||
/** | ||
* Build a transaction and send. | ||
* | ||
* @param transaction The command to execute | ||
* @param responseHandler The handler of the response object | ||
* @return A result promise of type T | ||
*/ | ||
public <T> CompletableFuture<T> submitNewTransaction( | ||
List<Command> transaction, RedisExceptionCheckedFunction<Response, T> responseHandler) { | ||
// register callback | ||
// create protobuf message from command | ||
// submit async call | ||
var transactionBuilder = Transaction.newBuilder(); | ||
for (var command : transaction) { | ||
transactionBuilder.addCommands( | ||
prepareRedisCommand(command.getRequestType(), command.getArguments())); | ||
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. how does this work routes? Each command gets a route, right? 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. According to protobuf - route set to the entire request 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. Ah. That makes sense. |
||
} | ||
|
||
return channel | ||
.write(RedisRequest.newBuilder().setTransaction(transactionBuilder.build()), true) | ||
.thenApplyAsync(responseHandler::apply); | ||
} | ||
|
||
/** | ||
|
@@ -42,30 +74,23 @@ public <T> CompletableFuture<T> submitNewCommand( | |
* @return An uncompleted request. CallbackDispatcher is responsible to complete it by adding a | ||
* callback id. | ||
*/ | ||
private RedisRequestOuterClass.RedisRequest.Builder prepareRedisRequest( | ||
private RedisRequestOuterClass.Command prepareRedisCommand( | ||
Command.RequestType command, String[] args) { | ||
RedisRequestOuterClass.Command.ArgsArray.Builder commandArgs = | ||
RedisRequestOuterClass.Command.ArgsArray.newBuilder(); | ||
ArgsArray.Builder commandArgs = ArgsArray.newBuilder(); | ||
for (var arg : args) { | ||
commandArgs.addArgs(arg); | ||
} | ||
|
||
return RedisRequestOuterClass.RedisRequest.newBuilder() | ||
.setSingleCommand( | ||
RedisRequestOuterClass.Command.newBuilder() | ||
.setRequestType(mapRequestTypes(command)) | ||
.setArgsArray(commandArgs.build()) | ||
.build()) | ||
.setRoute( | ||
RedisRequestOuterClass.Routes.newBuilder() | ||
.setSimpleRoutes(RedisRequestOuterClass.SimpleRoutes.AllNodes) | ||
.build()); | ||
return RedisRequestOuterClass.Command.newBuilder() | ||
.setRequestType(mapRequestTypes(command)) | ||
.setArgsArray(commandArgs.build()) | ||
.build(); | ||
} | ||
|
||
private RedisRequestOuterClass.RequestType mapRequestTypes(Command.RequestType inType) { | ||
private RequestType mapRequestTypes(Command.RequestType inType) { | ||
switch (inType) { | ||
case CUSTOM_COMMAND: | ||
return RedisRequestOuterClass.RequestType.CustomCommand; | ||
return RequestType.CustomCommand; | ||
} | ||
throw new RuntimeException("Unsupported request type"); | ||
} | ||
|
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.
why not run it in the same thread?