Skip to content

Commit

Permalink
Java: Add RENAMENX command. (#1332)
Browse files Browse the repository at this point in the history
* Add `RENAMENX` command. (#247)

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

* PR comments.

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

* Update IT.

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

---------

Signed-off-by: Yury-Fridlyand <[email protected]>
  • Loading branch information
Yury-Fridlyand authored Apr 29, 2024
1 parent b269d4d commit a37f6dc
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 1 deletion.
2 changes: 1 addition & 1 deletion glide-core/src/client/value_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ pub(crate) fn expected_type_for_cmd(cmd: &Cmd) -> Option<ExpectedReturnType> {
}
b"INCRBYFLOAT" | b"HINCRBYFLOAT" => Some(ExpectedReturnType::Double),
b"HEXISTS" | b"HSETNX" | b"EXPIRE" | b"EXPIREAT" | b"PEXPIRE" | b"PEXPIREAT"
| b"SISMEMBER" | b"PERSIST" | b"SMOVE" => Some(ExpectedReturnType::Boolean),
| b"SISMEMBER" | b"PERSIST" | b"SMOVE" | b"RENAMENX" => Some(ExpectedReturnType::Boolean),
b"SMISMEMBER" => Some(ExpectedReturnType::ArrayOfBools),
b"SMEMBERS" | b"SINTER" => 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 @@ -171,6 +171,7 @@ enum RequestType {
GeoPos = 128;
BZPopMax = 129;
ObjectFreq = 130;
RenameNx = 131;
Touch = 132;
}

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 @@ -139,6 +139,7 @@ pub enum RequestType {
GeoPos = 128,
BZPopMax = 129,
ObjectFreq = 130,
RenameNx = 131,
Touch = 132,
}

Expand Down Expand Up @@ -282,6 +283,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::LOLWUT => RequestType::LOLWUT,
ProtobufRequestType::GeoPos => RequestType::GeoPos,
ProtobufRequestType::BZPopMax => RequestType::BZPopMax,
ProtobufRequestType::RenameNx => RequestType::RenameNx,
ProtobufRequestType::Touch => RequestType::Touch,
}
}
Expand Down Expand Up @@ -421,6 +423,7 @@ impl RequestType {
RequestType::LOLWUT => Some(cmd("LOLWUT")),
RequestType::GeoPos => Some(cmd("GEOPOS")),
RequestType::BZPopMax => Some(cmd("BZPOPMAX")),
RequestType::RenameNx => Some(cmd("RENAMENX")),
RequestType::Touch => Some(cmd("TOUCH")),
}
}
Expand Down
7 changes: 7 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.RPop;
import static redis_request.RedisRequestOuterClass.RequestType.RPush;
import static redis_request.RedisRequestOuterClass.RequestType.RPushX;
import static redis_request.RedisRequestOuterClass.RequestType.RenameNx;
import static redis_request.RedisRequestOuterClass.RequestType.SAdd;
import static redis_request.RedisRequestOuterClass.RequestType.SCard;
import static redis_request.RedisRequestOuterClass.RequestType.SDiff;
Expand Down Expand Up @@ -368,6 +369,12 @@ public CompletableFuture<Long> objectRefcount(@NonNull String key) {
ObjectRefcount, new String[] {key}, this::handleLongOrNullResponse);
}

@Override
public CompletableFuture<Boolean> renamenx(@NonNull String key, @NonNull String newKey) {
return commandManager.submitNewCommand(
RenameNx, new String[] {key, newKey}, this::handleBooleanResponse);
}

@Override
public CompletableFuture<Long> incr(@NonNull String key) {
return commandManager.submitNewCommand(Incr, new String[] {key}, this::handleLongResponse);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,24 @@ CompletableFuture<Boolean> pexpireAt(
*/
CompletableFuture<Long> objectRefcount(String key);

/**
* Renames <code>key</code> to <code>newKey</code> if <code>newKey</code> does not yet exist.
*
* @apiNote When in cluster mode, both <code>key</code> and <code>newKey</code> must map to the
* same <code>hash slot</code>.
* @see <a href="https://redis.io/commands/renamenx/">redis.io</a> for details.
* @param key The key to rename.
* @param newKey The new key name.
* @return <code>true</code> if <code>key</code> was renamed to <code>newKey</code>, <code>false
* </code> if <code>newKey</code> already exists.
* @example
* <pre>{@code
* Boolean renamed = client.renamenx("old_key", "new_key").get();
* assert renamed;
* }</pre>
*/
CompletableFuture<Boolean> renamenx(String key, String newKey);

/**
* Updates the last access time of specified <code>keys</code>.
*
Expand Down
16 changes: 16 additions & 0 deletions java/client/src/main/java/glide/api/models/BaseTransaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.RPop;
import static redis_request.RedisRequestOuterClass.RequestType.RPush;
import static redis_request.RedisRequestOuterClass.RequestType.RPushX;
import static redis_request.RedisRequestOuterClass.RequestType.RenameNx;
import static redis_request.RedisRequestOuterClass.RequestType.SAdd;
import static redis_request.RedisRequestOuterClass.RequestType.SCard;
import static redis_request.RedisRequestOuterClass.RequestType.SDiff;
Expand Down Expand Up @@ -2127,6 +2128,21 @@ public T type(@NonNull String key) {
return getThis();
}

/**
* Renames <code>key</code> to <code>newKey</code> if <code>newKey</code> does not yet exist.
*
* @see <a href="https://redis.io/commands/renamenx/">redis.io</a> for details.
* @param key The key to rename.
* @param newKey The new key name.
* @return Command Response - <code>true</code> if <code>key</code> was renamed to <code>newKey
* </code>, <code>false</code> if <code>newKey</code> already exists.
*/
public T renamenx(@NonNull String key, @NonNull String newKey) {
ArgsArray commandArgs = buildArgs(key, newKey);
protobufTransaction.addCommands(buildCommand(RenameNx, commandArgs));
return getThis();
}

/**
* Inserts <code>element</code> in the list at <code>key</code> either before or after the <code>
* pivot</code>.
Expand Down
24 changes: 24 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.RPop;
import static redis_request.RedisRequestOuterClass.RequestType.RPush;
import static redis_request.RedisRequestOuterClass.RequestType.RPushX;
import static redis_request.RedisRequestOuterClass.RequestType.RenameNx;
import static redis_request.RedisRequestOuterClass.RequestType.SAdd;
import static redis_request.RedisRequestOuterClass.RequestType.SCard;
import static redis_request.RedisRequestOuterClass.RequestType.SDiff;
Expand Down Expand Up @@ -3150,6 +3151,29 @@ public void type_returns_success() {
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void renamenx_returns_success() {
// setup
String key = "key1";
String newKey = "key2";
String[] arguments = new String[] {key, newKey};

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

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

// exercise
CompletableFuture<Boolean> response = service.renamenx(key, newKey);

// verify
assertEquals(testResponse, response);
assertTrue(response.get());
}

@SneakyThrows
@Test
public void time_returns_success() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.RPop;
import static redis_request.RedisRequestOuterClass.RequestType.RPush;
import static redis_request.RedisRequestOuterClass.RequestType.RPushX;
import static redis_request.RedisRequestOuterClass.RequestType.RenameNx;
import static redis_request.RedisRequestOuterClass.RequestType.SAdd;
import static redis_request.RedisRequestOuterClass.RequestType.SCard;
import static redis_request.RedisRequestOuterClass.RequestType.SDiff;
Expand Down Expand Up @@ -490,6 +491,9 @@ InfScoreBound.NEGATIVE_INFINITY, new ScoreBoundary(3, false), new Limit(1, 2)),
transaction.type("key");
results.add(Pair.of(Type, buildArgs("key")));

transaction.renamenx("key", "newKey");
results.add(Pair.of(RenameNx, buildArgs("key", "newKey")));

transaction.linsert("key", AFTER, "pivot", "elem");
results.add(Pair.of(LInsert, buildArgs("key", "AFTER", "pivot", "elem")));

Expand Down
35 changes: 35 additions & 0 deletions java/integTest/src/test/java/glide/SharedCommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,41 @@ public void smove(BaseClient client) {
}
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
public void renamenx(BaseClient client) {
String key1 = "{key}" + UUID.randomUUID();
String key2 = "{key}" + UUID.randomUUID();
String key3 = "{key}" + UUID.randomUUID();

assertEquals(OK, client.set(key3, "key3").get());

// rename missing key
var executionException =
assertThrows(ExecutionException.class, () -> client.renamenx(key1, key2).get());
assertInstanceOf(RequestException.class, executionException.getCause());
assertTrue(executionException.getMessage().toLowerCase().contains("no such key"));

// rename a string
assertEquals(OK, client.set(key1, "key1").get());
assertTrue(client.renamenx(key1, key2).get());
assertFalse(client.renamenx(key2, key3).get());
assertEquals("key1", client.get(key2).get());
assertEquals(1, client.del(new String[] {key1, key2}).get());

// this one remains unchanged
assertEquals("key3", client.get(key3).get());

// same-slot requirement
if (client instanceof RedisClusterClient) {
executionException =
assertThrows(ExecutionException.class, () -> client.renamenx("abc", "zxy").get());
assertInstanceOf(RequestException.class, executionException.getCause());
assertTrue(executionException.getMessage().toLowerCase().contains("crossslot"));
}
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public static BaseTransaction<?> transactionTest(BaseTransaction<?> baseTransact
baseTransaction.set(key2, value2, SetOptions.builder().returnOldValue(true).build());
baseTransaction.strlen(key2);
baseTransaction.customCommand(new String[] {"MGET", key1, key2});
baseTransaction.renamenx(key1, key2);

baseTransaction.exists(new String[] {key1});
baseTransaction.persist(key1);
Expand Down Expand Up @@ -187,6 +188,7 @@ public static Object[] transactionTestResult() {
null,
(long) value1.length(), // strlen(key2)
new String[] {value1, value2},
false, // renamenx(key1, key2)
1L,
Boolean.FALSE, // persist(key1)
1L, // touch(new String[] {key1})
Expand Down

0 comments on commit a37f6dc

Please sign in to comment.