Skip to content

Commit

Permalink
Java: add MOVE command (#1519)
Browse files Browse the repository at this point in the history
Java: add MOVE command (#336)
  • Loading branch information
aaron-congo authored Jun 4, 2024
1 parent 1a15787 commit 4c6c081
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 12 deletions.
4 changes: 3 additions & 1 deletion glide-core/src/client/value_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,9 @@ pub(crate) fn expected_type_for_cmd(cmd: &Cmd) -> Option<ExpectedReturnType> {
}),
b"INCRBYFLOAT" | b"HINCRBYFLOAT" | b"ZINCRBY" => Some(ExpectedReturnType::Double),
b"HEXISTS" | b"HSETNX" | b"EXPIRE" | b"EXPIREAT" | b"PEXPIRE" | b"PEXPIREAT"
| b"SISMEMBER" | b"PERSIST" | b"SMOVE" | b"RENAMENX" => Some(ExpectedReturnType::Boolean),
| b"SISMEMBER" | b"PERSIST" | b"SMOVE" | b"RENAMENX" | b"MOVE" => {
Some(ExpectedReturnType::Boolean)
}
b"SMISMEMBER" => Some(ExpectedReturnType::ArrayOfBools),
b"SMEMBERS" | b"SINTER" | b"SDIFF" => Some(ExpectedReturnType::Set),
b"ZSCORE" | b"GEODIST" => Some(ExpectedReturnType::DoubleOrNull),
Expand Down
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 @@ -204,6 +204,7 @@ enum RequestType {
SRandMember = 171;
BitField = 172;
BitFieldReadOnly = 173;
Move = 174;
SInterCard = 175;
}

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 @@ -174,6 +174,7 @@ pub enum RequestType {
SRandMember = 171,
BitField = 172,
BitFieldReadOnly = 173,
Move = 174,
SInterCard = 175,
}

Expand Down Expand Up @@ -352,6 +353,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::SRandMember => RequestType::SRandMember,
ProtobufRequestType::BitField => RequestType::BitField,
ProtobufRequestType::BitFieldReadOnly => RequestType::BitFieldReadOnly,
ProtobufRequestType::Move => RequestType::Move,
ProtobufRequestType::SInterCard => RequestType::SInterCard,
}
}
Expand Down Expand Up @@ -526,6 +528,7 @@ impl RequestType {
RequestType::SRandMember => Some(cmd("SRANDMEMBER")),
RequestType::BitField => Some(cmd("BITFIELD")),
RequestType::BitFieldReadOnly => Some(cmd("BITFIELD_RO")),
RequestType::Move => Some(cmd("MOVE")),
RequestType::SInterCard => Some(cmd("SINTERCARD")),
}
}
Expand Down
7 changes: 7 additions & 0 deletions java/client/src/main/java/glide/api/RedisClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Info;
import static redis_request.RedisRequestOuterClass.RequestType.LastSave;
import static redis_request.RedisRequestOuterClass.RequestType.Lolwut;
import static redis_request.RedisRequestOuterClass.RequestType.Move;
import static redis_request.RedisRequestOuterClass.RequestType.Ping;
import static redis_request.RedisRequestOuterClass.RequestType.Select;
import static redis_request.RedisRequestOuterClass.RequestType.Time;
Expand Down Expand Up @@ -196,4 +197,10 @@ public CompletableFuture<String> functionLoad(@NonNull String libraryCode, boole
replace ? new String[] {REPLACE.toString(), libraryCode} : new String[] {libraryCode};
return commandManager.submitNewCommand(FunctionLoad, arguments, this::handleStringResponse);
}

@Override
public CompletableFuture<Boolean> move(@NonNull String key, long dbIndex) {
return commandManager.submitNewCommand(
Move, new String[] {key, Long.toString(dbIndex)}, this::handleBooleanResponse);
}
}
18 changes: 18 additions & 0 deletions java/client/src/main/java/glide/api/commands/GenericCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,22 @@ public interface GenericCommands {
* }</pre>
*/
CompletableFuture<Object[]> exec(Transaction transaction);

/**
* Move <code>key</code> from the currently selected database to the database specified by <code>
* dbIndex</code>.
*
* @see <a href="https://redis.io/commands/move/">redis.io</a> for more details.
* @param key The key to move.
* @param dbIndex The index of the database to move <code>key</code> to.
* @return <code>true</code> if <code>key</code> was moved, or <code>false</code> if the <code>key
* </code> already exists in the destination database or does not exist in the source
* database.
* @example
* <pre>{@code
* Boolean moved = client.move("some_key", 1L).get();
* assert moved;
* }</pre>
*/
CompletableFuture<Boolean> move(String key, long dbIndex);
}
22 changes: 20 additions & 2 deletions java/client/src/main/java/glide/api/models/Transaction.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.models;

import static redis_request.RedisRequestOuterClass.RequestType.Move;
import static redis_request.RedisRequestOuterClass.RequestType.Select;

import lombok.AllArgsConstructor;
import redis_request.RedisRequestOuterClass;
import redis_request.RedisRequestOuterClass.Command.ArgsArray;

/**
* Extends BaseTransaction class for Redis standalone commands. Transactions allow the execution of
Expand Down Expand Up @@ -41,9 +42,26 @@ protected Transaction getThis() {
* @return Command Response - A simple <code>OK</code> response.
*/
public Transaction select(long index) {
RedisRequestOuterClass.Command.ArgsArray commandArgs = buildArgs(Long.toString(index));
ArgsArray commandArgs = buildArgs(Long.toString(index));

protobufTransaction.addCommands(buildCommand(Select, commandArgs));
return this;
}

/**
* Move <code>key</code> from the currently selected database to the database specified by <code>
* dbIndex</code>.
*
* @see <a href="https://redis.io/commands/move/">redis.io</a> for more details.
* @param key The key to move.
* @param dbIndex The index of the database to move <code>key</code> to.
* @return Command Response - <code>true</code> if <code>key</code> was moved, or <code>false
* </code> if the <code>key</code> already exists in the destination database or does not
* exist in the source database.
*/
public Transaction move(String key, long dbIndex) {
ArgsArray commandArgs = buildArgs(key, Long.toString(dbIndex));
protobufTransaction.addCommands(buildCommand(Move, commandArgs));
return this;
}
}
26 changes: 26 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Lolwut;
import static redis_request.RedisRequestOuterClass.RequestType.MGet;
import static redis_request.RedisRequestOuterClass.RequestType.MSet;
import static redis_request.RedisRequestOuterClass.RequestType.Move;
import static redis_request.RedisRequestOuterClass.RequestType.ObjectEncoding;
import static redis_request.RedisRequestOuterClass.RequestType.ObjectFreq;
import static redis_request.RedisRequestOuterClass.RequestType.ObjectIdleTime;
Expand Down Expand Up @@ -5557,4 +5558,29 @@ public void bitfield_returns_success() {
assertEquals(testResponse, response);
assertEquals(result, payload);
}

@SneakyThrows
@Test
public void move_returns_success() {
// setup
String key = "testKey";
long dbIndex = 2L;
String[] arguments = new String[] {key, Long.toString(dbIndex)};
Boolean value = true;

CompletableFuture<Boolean> testResponse = new CompletableFuture<>();
testResponse.complete(value);

// match on protobuf request
when(commandManager.<Boolean>submitNewCommand(eq(Move), eq(arguments), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Boolean> response = service.move(key, dbIndex);
Boolean payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(value, payload);
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.models;

import static glide.api.models.TransactionTests.buildArgs;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static redis_request.RedisRequestOuterClass.RequestType.Move;
import static redis_request.RedisRequestOuterClass.RequestType.Select;

import com.google.protobuf.ByteString;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.Test;
import redis_request.RedisRequestOuterClass;
import redis_request.RedisRequestOuterClass.Command.ArgsArray;

public class StandaloneTransactionTests {
@Test
Expand All @@ -20,8 +20,9 @@ public void standalone_transaction_commands() {
Transaction transaction = new Transaction();

transaction.select(5L);
results.add(
Pair.of(Select, ArgsArray.newBuilder().addArgs(ByteString.copyFromUtf8("5")).build()));
results.add(Pair.of(Select, buildArgs("5")));
transaction.move("testKey", 2L);
results.add(Pair.of(Move, buildArgs("testKey", "2")));

var protobufTransaction = transaction.getProtobufTransaction().build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,7 @@ InfScoreBound.NEGATIVE_INFINITY, new ScoreBoundary(3, false), new Limit(1, 2)),
}
}

private ArgsArray buildArgs(String... args) {
static ArgsArray buildArgs(String... args) {
var builder = ArgsArray.newBuilder();
for (var arg : args) {
builder.addArgs(ByteString.copyFromUtf8(arg));
Expand Down
30 changes: 30 additions & 0 deletions java/integTest/src/test/java/glide/standalone/CommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,36 @@ public void select_test_gives_error() {
assertTrue(e.getCause() instanceof RequestException);
}

@Test
@SneakyThrows
public void move() {
String key1 = UUID.randomUUID().toString();
String key2 = UUID.randomUUID().toString();
String value1 = UUID.randomUUID().toString();
String value2 = UUID.randomUUID().toString();
String nonExistingKey = UUID.randomUUID().toString();
assertEquals(false, regularClient.move(nonExistingKey, 1L).get());

assertEquals(OK, regularClient.select(0).get());
assertEquals(OK, regularClient.set(key1, value1).get());
assertEquals(OK, regularClient.set(key2, value2).get());
assertEquals(true, regularClient.move(key1, 1L).get());
assertNull(regularClient.get(key1).get());

assertEquals(OK, regularClient.select(1).get());
assertEquals(value1, regularClient.get(key1).get());

assertEquals(OK, regularClient.set(key2, value2).get());
// Move does not occur because key2 already exists in DB 0
assertEquals(false, regularClient.move(key2, 0).get());
assertEquals(value2, regularClient.get(key2).get());

// Incorrect argument - DB index must be non-negative
ExecutionException e =
assertThrows(ExecutionException.class, () -> regularClient.move(key1, -1L).get());
assertTrue(e.getCause() instanceof RequestException);
}

@Test
@SneakyThrows
public void clientId() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,23 @@ public void test_standalone_transaction() {
String key = UUID.randomUUID().toString();
String value = UUID.randomUUID().toString();

Transaction transaction =
new Transaction().select(1).set(key, value).get(key).select(0).get(key);

Object[] expectedResult = new Object[] {OK, OK, value, OK, null};
Transaction transaction = new Transaction();
transaction.set(key, value);
transaction.get(key);
transaction.move(key, 1L);
transaction.get(key);
transaction.select(1);
transaction.get(key);

Object[] expectedResult =
new Object[] {
OK, // transaction.set(key, value);
value, // transaction.get(key);
true, // transaction.move(key, 1L);
null, // transaction.get(key);
OK, // transaction.select(1);
value // transaction.get(key);
};

Object[] result = client.exec(transaction).get();
assertArrayEquals(expectedResult, result);
Expand Down

0 comments on commit 4c6c081

Please sign in to comment.