Skip to content

Commit

Permalink
Java: Add FCALL_RO command. (#1577)
Browse files Browse the repository at this point in the history
* Java: Add `FCALL_RO` command. (#365)

* Add `FCALL_RO` command.

Signed-off-by: Yury-Fridlyand <[email protected]>

* Fix tests and update docs.

Signed-off-by: Yury-Fridlyand <[email protected]>

* Typo fix.

Signed-off-by: Yury-Fridlyand <[email protected]>

* Typo fix.

Signed-off-by: Yury-Fridlyand <[email protected]>

---------

Signed-off-by: Yury-Fridlyand <[email protected]>

* Update submodule.

Signed-off-by: Yury-Fridlyand <[email protected]>

---------

Signed-off-by: Yury-Fridlyand <[email protected]>
  • Loading branch information
Yury-Fridlyand authored Jun 17, 2024
1 parent 7df2498 commit 63ce1b1
Show file tree
Hide file tree
Showing 16 changed files with 476 additions and 46 deletions.
1 change: 1 addition & 0 deletions glide-core/src/protobuf/redis_request.proto
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ enum RequestType {
Sort = 160;
FunctionKill = 161;
FunctionStats = 162;
FCallReadOnly = 163;
LSet = 165;
XDel = 166;
XRange = 167;
Expand Down
3 changes: 3 additions & 0 deletions glide-core/src/request_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ pub enum RequestType {
Sort = 160,
FunctionKill = 161,
FunctionStats = 162,
FCallReadOnly = 163,
LSet = 165,
XDel = 166,
XRange = 167,
Expand Down Expand Up @@ -363,6 +364,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::XLen => RequestType::XLen,
ProtobufRequestType::FunctionKill => RequestType::FunctionKill,
ProtobufRequestType::FunctionStats => RequestType::FunctionStats,
ProtobufRequestType::FCallReadOnly => RequestType::FCallReadOnly,
ProtobufRequestType::LSet => RequestType::LSet,
ProtobufRequestType::XDel => RequestType::XDel,
ProtobufRequestType::XRange => RequestType::XRange,
Expand Down Expand Up @@ -551,6 +553,7 @@ impl RequestType {
RequestType::XLen => Some(cmd("XLEN")),
RequestType::FunctionKill => Some(get_two_word_command("FUNCTION", "KILL")),
RequestType::FunctionStats => Some(get_two_word_command("FUNCTION", "STATS")),
RequestType::FCallReadOnly => Some(cmd("FCALL_RO")),
RequestType::LSet => Some(cmd("LSET")),
RequestType::XDel => Some(cmd("XDEL")),
RequestType::XRange => Some(cmd("XRANGE")),
Expand Down
9 changes: 9 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.ExpireAt;
import static redis_request.RedisRequestOuterClass.RequestType.ExpireTime;
import static redis_request.RedisRequestOuterClass.RequestType.FCall;
import static redis_request.RedisRequestOuterClass.RequestType.FCallReadOnly;
import static redis_request.RedisRequestOuterClass.RequestType.GeoAdd;
import static redis_request.RedisRequestOuterClass.RequestType.GeoDist;
import static redis_request.RedisRequestOuterClass.RequestType.GeoHash;
Expand Down Expand Up @@ -1830,6 +1831,14 @@ public CompletableFuture<Object> fcall(
return commandManager.submitNewCommand(FCall, args, this::handleObjectOrNullResponse);
}

@Override
public CompletableFuture<Object> fcallReadOnly(
@NonNull String function, @NonNull String[] keys, @NonNull String[] arguments) {
String[] args =
concatenateArrays(new String[] {function, Long.toString(keys.length)}, keys, arguments);
return commandManager.submitNewCommand(FCallReadOnly, args, this::handleObjectOrNullResponse);
}

@Override
public CompletableFuture<Boolean> copy(
@NonNull String source, @NonNull String destination, boolean replace) {
Expand Down
5 changes: 5 additions & 0 deletions java/client/src/main/java/glide/api/RedisClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ public CompletableFuture<Object> fcall(@NonNull String function) {
return fcall(function, new String[0], new String[0]);
}

@Override
public CompletableFuture<Object> fcallReadOnly(@NonNull String function) {
return fcallReadOnly(function, new String[0], new String[0]);
}

@Override
public CompletableFuture<Boolean> copy(
@NonNull String source, @NonNull String destination, long destinationDB) {
Expand Down
33 changes: 33 additions & 0 deletions java/client/src/main/java/glide/api/RedisClusterClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.DBSize;
import static redis_request.RedisRequestOuterClass.RequestType.Echo;
import static redis_request.RedisRequestOuterClass.RequestType.FCall;
import static redis_request.RedisRequestOuterClass.RequestType.FCallReadOnly;
import static redis_request.RedisRequestOuterClass.RequestType.FlushAll;
import static redis_request.RedisRequestOuterClass.RequestType.FunctionDelete;
import static redis_request.RedisRequestOuterClass.RequestType.FunctionFlush;
Expand Down Expand Up @@ -578,6 +579,38 @@ public CompletableFuture<ClusterValue<Object>> fcall(
: ClusterValue.ofMultiValue(handleMapResponse(response)));
}

@Override
public CompletableFuture<Object> fcallReadOnly(@NonNull String function) {
return fcallReadOnly(function, new String[0]);
}

@Override
public CompletableFuture<ClusterValue<Object>> fcallReadOnly(
@NonNull String function, @NonNull Route route) {
return fcallReadOnly(function, new String[0], route);
}

@Override
public CompletableFuture<Object> fcallReadOnly(
@NonNull String function, @NonNull String[] arguments) {
String[] args = concatenateArrays(new String[] {function, "0"}, arguments); // 0 - key count
return commandManager.submitNewCommand(FCallReadOnly, args, this::handleObjectOrNullResponse);
}

@Override
public CompletableFuture<ClusterValue<Object>> fcallReadOnly(
@NonNull String function, @NonNull String[] arguments, @NonNull Route route) {
String[] args = concatenateArrays(new String[] {function, "0"}, arguments); // 0 - key count
return commandManager.submitNewCommand(
FCallReadOnly,
args,
route,
response ->
route instanceof SingleNodeRoute
? ClusterValue.ofSingleValue(handleObjectOrNullResponse(response))
: ClusterValue.ofMultiValue(handleMapResponse(response)));
}

@Override
public CompletableFuture<String> functionKill() {
return commandManager.submitNewCommand(FunctionKill, new String[0], this::handleStringResponse);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.commands;

import glide.api.models.configuration.ReadFrom;
import java.util.concurrent.CompletableFuture;

/**
Expand All @@ -13,12 +14,14 @@
public interface ScriptingAndFunctionsBaseCommands {

/**
* Invokes a previously loaded function.
* Invokes a previously loaded function.<br>
* This command is routed to primary nodes only.<br>
* To route to a replica please refer to {@link #fcallReadOnly}.
*
* @apiNote When in cluster mode
* <ul>
* <li>all <code>keys</code> must map to the same hash slot.
* <li>if no <code>keys</code> are given, command will be routed to a random node.
* <li>if no <code>keys</code> are given, command will be routed to a random primary node.
* </ul>
*
* @since Redis 7.0 and above.
Expand All @@ -27,7 +30,7 @@ public interface ScriptingAndFunctionsBaseCommands {
* @param keys An <code>array</code> of keys accessed by the function. To ensure the correct
* execution of functions, both in standalone and clustered deployments, all names of keys
* that a function accesses must be explicitly provided as <code>keys</code>.
* @param arguments An <code>array</code> of <code>function</code> arguments. <code>Arguments
* @param arguments An <code>array</code> of <code>function</code> arguments. <code>arguments
* </code> should not represent names of keys.
* @return The invoked function's return value.
* @example
Expand All @@ -38,4 +41,32 @@ public interface ScriptingAndFunctionsBaseCommands {
* }</pre>
*/
CompletableFuture<Object> fcall(String function, String[] keys, String[] arguments);

/**
* Invokes a previously loaded read-only function.<br>
* This command is routed depending on the client's {@link ReadFrom} strategy.
*
* @apiNote When in cluster mode
* <ul>
* <li>all <code>keys</code> must map to the same hash slot.
* <li>if no <code>keys</code> are given, command will be routed to a random node.
* </ul>
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/fcall_ro/">redis.io</a> for details.
* @param function The function name.
* @param keys An <code>array</code> of keys accessed by the function. To ensure the correct
* execution of functions, both in standalone and clustered deployments, all names of keys
* that a function accesses must be explicitly provided as <code>keys</code>.
* @param arguments An <code>array</code> of <code>function</code> arguments. <code>arguments
* </code> should not represent names of keys.
* @return The invoked function's return value.
* @example
* <pre>{@code
* String[] args = new String[] { "Answer", "to", "the", "Ultimate", "Question", "of", "Life,", "the", "Universe,", "and", "Everything"};
* Object response = client.fcallReadOnly("Deep_Thought", new String[0], args).get();
* assert response == 42L;
* }</pre>
*/
CompletableFuture<Object> fcallReadOnly(String function, String[] keys, String[] arguments);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import glide.api.models.ClusterValue;
import glide.api.models.commands.FlushMode;
import glide.api.models.configuration.ReadFrom;
import glide.api.models.configuration.RequestRoutingConfiguration.Route;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -268,7 +269,8 @@ CompletableFuture<ClusterValue<Map<String, Object>[]>> functionList(

/**
* Invokes a previously loaded function.<br>
* The command will be routed to a random node.
* The command will be routed to a primary random node.<br>
* To route to a replica please refer to {@link #fcallReadOnly(String)}.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/fcall/">redis.io</a> for details.
Expand Down Expand Up @@ -303,12 +305,13 @@ CompletableFuture<ClusterValue<Map<String, Object>[]>> functionList(

/**
* Invokes a previously loaded function.<br>
* The command will be routed to a random node.
* The command will be routed to a random primary node.<br>
* To route to a replica please refer to {@link #fcallReadOnly(String, String[])}.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/fcall/">redis.io</a> for details.
* @param function The function name.
* @param arguments An <code>array</code> of <code>function</code> arguments. <code>Arguments
* @param arguments An <code>array</code> of <code>function</code> arguments. <code>arguments
* </code> should not represent names of keys.
* @return The invoked function's return value.
* @example
Expand All @@ -326,7 +329,7 @@ CompletableFuture<ClusterValue<Map<String, Object>[]>> functionList(
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/fcall/">redis.io</a> for details.
* @param function The function name.
* @param arguments An <code>array</code> of <code>function</code> arguments. <code>Arguments
* @param arguments An <code>array</code> of <code>function</code> arguments. <code>arguments
* </code> should not represent names of keys.
* @param route Specifies the routing configuration for the command. The client will route the
* command to the nodes defined by <code>route</code>.
Expand All @@ -340,6 +343,81 @@ CompletableFuture<ClusterValue<Map<String, Object>[]>> functionList(
*/
CompletableFuture<ClusterValue<Object>> fcall(String function, String[] arguments, Route route);

/**
* Invokes a previously loaded read-only function.<br>
* The command is routed to a random node depending on the client's {@link ReadFrom} strategy.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/fcall_ro/">redis.io</a> for details.
* @param function The function name.
* @return The invoked function's return value.
* @example
* <pre>{@code
* Object response = client.fcallReadOnly("Deep_Thought").get();
* assert response == 42L;
* }</pre>
*/
CompletableFuture<Object> fcallReadOnly(String function);

/**
* Invokes a previously loaded read-only function.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/fcall_ro/">redis.io</a> for details.
* @param function The function name.
* @param route Specifies the routing configuration for the command. The client will route the
* command to the nodes defined by <code>route</code>.
* @return The invoked function's return value wrapped by a {@link ClusterValue}.
* @example
* <pre>{@code
* ClusterValue<Object> response = client.fcallReadOnly("Deep_Thought", ALL_NODES).get();
* for (Object nodeResponse : response.getMultiValue().values()) {
* assert nodeResponse == 42L;
* }
* }</pre>
*/
CompletableFuture<ClusterValue<Object>> fcallReadOnly(String function, Route route);

/**
* Invokes a previously loaded function.<br>
* The command is routed to a random node depending on the client's {@link ReadFrom} strategy.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/fcall_ro/">redis.io</a> for details.
* @param function The function name.
* @param arguments An <code>array</code> of <code>function</code> arguments. <code>arguments
* </code> should not represent names of keys.
* @return The invoked function's return value.
* @example
* <pre>{@code
* String[] args = new String[] { "Answer", "to", "the", "Ultimate", "Question", "of", "Life,", "the", "Universe,", "and", "Everything" };
* Object response = client.fcallReadOnly("Deep_Thought", args).get();
* assert response == 42L;
* }</pre>
*/
CompletableFuture<Object> fcallReadOnly(String function, String[] arguments);

/**
* Invokes a previously loaded read-only function.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/fcall_ro/">redis.io</a> for details.
* @param function The function name.
* @param arguments An <code>array</code> of <code>function</code> arguments. <code>arguments
* </code> should not represent names of keys.
* @param route Specifies the routing configuration for the command. The client will route the
* command to the nodes defined by <code>route</code>.
* @return The invoked function's return value wrapped by a {@link ClusterValue}.
* @example
* <pre>{@code
* String[] args = new String[] { "Answer", "to", "the", "Ultimate", "Question", "of", "Life,", "the", "Universe,", "and", "Everything" };
* ClusterValue<Object> response = client.fcallReadOnly("Deep_Thought", args, RANDOM).get();
* assert response.getSingleValue() == 42L;
* }</pre>
*/
CompletableFuture<ClusterValue<Object>> fcallReadOnly(
String function, String[] arguments, Route route);

/**
* Kills a function that is currently executing.<br>
* <code>FUNCTION KILL</code> terminates read-only functions only.<br>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package glide.api.commands;

import glide.api.models.commands.FlushMode;
import glide.api.models.configuration.ReadFrom;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

Expand Down Expand Up @@ -129,7 +130,9 @@ public interface ScriptingAndFunctionsCommands {
CompletableFuture<String> functionDelete(String libName);

/**
* Invokes a previously loaded function.
* Invokes a previously loaded function.<br>
* This command is routed to primary nodes only.<br>
* To route to a replica please refer to {@link #fcallReadOnly}.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/fcall/">redis.io</a> for details.
Expand All @@ -143,6 +146,22 @@ public interface ScriptingAndFunctionsCommands {
*/
CompletableFuture<Object> fcall(String function);

/**
* Invokes a previously loaded read-only function.<br>
* This command is routed depending on the client's {@link ReadFrom} strategy.
*
* @since Redis 7.0 and above.
* @see <a href="https://redis.io/docs/latest/commands/fcall_ro/">redis.io</a> for details.
* @param function The function name.
* @return The invoked function's return value.
* @example
* <pre>{@code
* Object response = client.fcallReadOnly("Deep_Thought").get();
* assert response == 42L;
* }</pre>
*/
CompletableFuture<Object> fcallReadOnly(String function);

/**
* Kills a function that is currently executing.<br>
* <code>FUNCTION KILL</code> terminates read-only functions only.
Expand Down
Loading

0 comments on commit 63ce1b1

Please sign in to comment.