Skip to content

Commit

Permalink
support mset and msetnx with GlideString (#1842)
Browse files Browse the repository at this point in the history
* support mset and msetnx with GlideString

* fix comments
  • Loading branch information
alon-arenberg authored Jul 7, 2024
1 parent e318665 commit 929e667
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 16 deletions.
13 changes: 13 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,12 @@ public CompletableFuture<String> mset(@NonNull Map<String, String> keyValueMap)
return commandManager.submitNewCommand(MSet, args, this::handleStringResponse);
}

@Override
public CompletableFuture<String> msetBinary(@NonNull Map<GlideString, GlideString> keyValueMap) {
GlideString[] args = convertMapToKeyValueGlideStringArray(keyValueMap);
return commandManager.submitNewCommand(MSet, args, this::handleStringResponse);
}

@Override
public CompletableFuture<String> objectEncoding(@NonNull String key) {
return commandManager.submitNewCommand(
Expand Down Expand Up @@ -4022,6 +4028,13 @@ public CompletableFuture<Boolean> msetnx(@NonNull Map<String, String> keyValueMa
return commandManager.submitNewCommand(MSetNX, args, this::handleBooleanResponse);
}

@Override
public CompletableFuture<Boolean> msetnxBinary(
@NonNull Map<GlideString, GlideString> keyValueMap) {
GlideString[] args = convertMapToKeyValueGlideStringArray(keyValueMap);
return commandManager.submitNewCommand(MSetNX, args, this::handleBooleanResponse);
}

@Override
public CompletableFuture<String> lcs(@NonNull String key1, @NonNull String key2) {
String[] arguments = new String[] {key1, key2};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,22 @@ public interface StringBaseCommands {
*/
CompletableFuture<String> mset(Map<String, String> keyValueMap);

/**
* Sets multiple keys to multiple values in a single operation.
*
* @apiNote When in cluster mode, the command may route to multiple nodes when keys in <code>
* keyValueMap</code> map to different hash slots.
* @see <a href="https://valkey.io/commands/mset/">valkey.io</a> for details.
* @param keyValueMap A key-value map consisting of keys and their respective values to set.
* @return Always <code>OK</code>.
* @example
* <pre>{@code
* String result = client.msetBinary(Map.of(gs("key1"), gs("value1"), gs("key2"), gs("value2")}).get();
* assert result.equals("OK"));
* }</pre>
*/
CompletableFuture<String> msetBinary(Map<GlideString, GlideString> keyValueMap);

/**
* Sets multiple keys to values if the key does not exist. The operation is atomic, and if one or
* more keys already exist, the entire operation fails.
Expand All @@ -317,6 +333,23 @@ public interface StringBaseCommands {
*/
CompletableFuture<Boolean> msetnx(Map<String, String> keyValueMap);

/**
* Sets multiple keys to values if the key does not exist. The operation is atomic, and if one or
* more keys already exist, the entire operation fails.
*
* @apiNote When in cluster mode, all keys in <code>keyValueMap</code> must map to the same hash
* slot.
* @see <a href="https://valkey.io/commands/msetnx/">valkey.io</a> for details.
* @param keyValueMap A key-value map consisting of keys and their respective values to set.
* @return <code>true</code> if all keys were set. <code>false</code> if no key was set.
* @example
* <pre>{@code
* Boolean result = client.msetnxBinary(Map.of(gs("key1"), gs("value1"), gs("key2"), gs("value2")}).get();
* assert result;
* }</pre>
*/
CompletableFuture<Boolean> msetnxBinary(Map<GlideString, GlideString> keyValueMap);

/**
* Increments the number stored at <code>key</code> by one. If <code>key</code> does not exist, it
* is set to 0 before performing the operation.
Expand Down
51 changes: 51 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1750,6 +1750,31 @@ public void mset_returns_success() {
assertEquals(OK, payload);
}

@SneakyThrows
@Test
public void mset_binary_returns_success() {
// setup
Map<GlideString, GlideString> keyValueMap = new LinkedHashMap<>();
keyValueMap.put(gs("key1"), gs("value1"));
keyValueMap.put(gs("key2"), gs("value2"));
GlideString[] args = {gs("key1"), gs("value1"), gs("key2"), gs("value2")};

CompletableFuture<String> testResponse = new CompletableFuture<>();
testResponse.complete(OK);

// match on protobuf request
when(commandManager.<String>submitNewCommand(eq(MSet), eq(args), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<String> response = service.msetBinary(keyValueMap);
String payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(OK, payload);
}

@SneakyThrows
@Test
public void msetnx_returns_success() {
Expand All @@ -1776,6 +1801,32 @@ public void msetnx_returns_success() {
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void msetnx_binary_returns_success() {
// setup
Map<GlideString, GlideString> keyValueMap = new LinkedHashMap<>();
keyValueMap.put(gs("key1"), gs("value1"));
keyValueMap.put(gs("key2"), gs("value2"));
GlideString[] args = {gs("key1"), gs("value1"), gs("key2"), gs("value2")};
Boolean value = true;

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

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

// exercise
CompletableFuture<Boolean> response = service.msetnxBinary(keyValueMap);
Boolean payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void incr_returns_success() {
Expand Down
58 changes: 50 additions & 8 deletions java/integTest/src/test/java/glide/SharedCommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -621,21 +621,39 @@ public void mset_mget_existing_non_existing_key(BaseClient client) {
client.mget(new String[] {key1, key2, nonExisting, key3}).get());
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
public void mset_mget_existing_non_existing_key_binary(BaseClient client) {
// keys are from different slots
GlideString key1 = gs(UUID.randomUUID().toString());
GlideString key2 = gs(UUID.randomUUID().toString());
GlideString key3 = gs(UUID.randomUUID().toString());
GlideString nonExisting = gs(UUID.randomUUID().toString());
GlideString value = gs(UUID.randomUUID().toString());
Map<GlideString, GlideString> keyValueMap = Map.of(key1, value, key2, value, key3, value);

assertEquals(OK, client.msetBinary(keyValueMap).get());
assertArrayEquals(
new GlideString[] {value, value, null, value},
client.mget(new GlideString[] {key1, key2, nonExisting, key3}).get());
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
public void mset_mget_binary(BaseClient client) {
// keys are from different slots
String key1 = UUID.randomUUID().toString();
String key2 = UUID.randomUUID().toString();
String key3 = UUID.randomUUID().toString();
String value = UUID.randomUUID().toString();
Map<String, String> keyValueMap = Map.of(key1, value, key2, value, key3, value);
GlideString key1 = gs(UUID.randomUUID().toString());
GlideString key2 = gs(UUID.randomUUID().toString());
GlideString key3 = gs(UUID.randomUUID().toString());
GlideString value = gs(UUID.randomUUID().toString());
Map<GlideString, GlideString> keyValueMap = Map.of(key1, value, key2, value, key3, value);

assertEquals(OK, client.mset(keyValueMap).get());
assertEquals(OK, client.msetBinary(keyValueMap).get());
assertArrayEquals(
new GlideString[] {gs(value), gs(value), gs(value)},
client.mget(new GlideString[] {gs(key1), gs(key2), gs(key3)}).get());
new GlideString[] {value, value, value},
client.mget(new GlideString[] {key1, key2, key3}).get());
}

@SneakyThrows
Expand Down Expand Up @@ -10821,6 +10839,30 @@ public void msetnx(BaseClient client) {
assertNull(client.get(key3).get());
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
public void msetnx_binary(BaseClient client) {
// keys are from different slots
GlideString key1 = gs("{key}-1" + UUID.randomUUID());
GlideString key2 = gs("{key}-2" + UUID.randomUUID());
GlideString key3 = gs("{key}-3" + UUID.randomUUID());
GlideString nonExisting = gs(UUID.randomUUID().toString());
GlideString value = gs(UUID.randomUUID().toString());
Map<GlideString, GlideString> keyValueMap1 = Map.of(key1, value, key2, value);
Map<GlideString, GlideString> keyValueMap2 = Map.of(key2, value, key3, value);

// all keys are empty, successfully set
assertTrue(client.msetnxBinary(keyValueMap1).get());
assertArrayEquals(
new GlideString[] {value, value, null},
client.mget(new GlideString[] {key1, key2, nonExisting}).get());

// one of the keys is already set, nothing gets set
assertFalse(client.msetnxBinary(keyValueMap2).get());
assertNull(client.get(key3).get());
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
Expand Down
18 changes: 10 additions & 8 deletions java/integTest/src/test/java/glide/standalone/CommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -1563,9 +1563,10 @@ public void scan_binary() {
GlideString initialCursor = gs("0");

int numberKeys = 500;
Map<String, String> keys = new HashMap<>();
Map<GlideString, GlideString> keys = new HashMap<>();
for (int i = 0; i < numberKeys; i++) {
keys.put("{key}-" + i + "-" + UUID.randomUUID(), "{value}-" + i + "-" + UUID.randomUUID());
keys.put(
gs("{key}-" + i + "-" + UUID.randomUUID()), gs("{value}-" + i + "-" + UUID.randomUUID()));
}

int resultCursorIndex = 0;
Expand All @@ -1585,7 +1586,7 @@ public void scan_binary() {
assertDeepEquals(new String[] {}, negativeResult[resultCollectionIndex]);

// Add keys to the database using mset
regularClient.mset(keys).get();
regularClient.msetBinary(keys).get();

Object[] result;
Object[] keysFound = new GlideString[0];
Expand All @@ -1607,7 +1608,7 @@ public void scan_binary() {

// check that each key added to the database is found through the cursor
Object[] finalKeysFound = keysFound;
keys.forEach((key, value) -> assertTrue(ArrayUtils.contains(finalKeysFound, gs(key))));
keys.forEach((key, value) -> assertTrue(ArrayUtils.contains(finalKeysFound, key)));
}

@Test
Expand Down Expand Up @@ -1701,11 +1702,12 @@ public void scan_binary_with_options() {
int resultCollectionIndex = 1;

// Add string keys to the database using mset
Map<String, String> stringKeys = new HashMap<>();
Map<GlideString, GlideString> stringKeys = new HashMap<>();
for (int i = 0; i < 10; i++) {
stringKeys.put("{key}-" + i + "-" + matchPattern, "{value}-" + i + "-" + matchPattern);
stringKeys.put(
gs("{key}-" + i + "-" + matchPattern), gs("{value}-" + i + "-" + matchPattern));
}
regularClient.mset(stringKeys).get();
regularClient.msetBinary(stringKeys).get();

// Add set keys to the database using sadd
List<GlideString> setKeys = new ArrayList<>();
Expand Down Expand Up @@ -1743,7 +1745,7 @@ public void scan_binary_with_options() {

// check that each key added to the database is found through the cursor
Object[] finalKeysFound = keysFound;
stringKeys.forEach((key, value) -> assertTrue(ArrayUtils.contains(finalKeysFound, gs(key))));
stringKeys.forEach((key, value) -> assertTrue(ArrayUtils.contains(finalKeysFound, key)));

// scan for sets by match pattern:
options = ScanOptions.builder().matchPattern("*" + matchPattern).count(100L).type(SET).build();
Expand Down

0 comments on commit 929e667

Please sign in to comment.