Skip to content

Commit

Permalink
Java: Add command BLMPOP (valkey-io#1464)
Browse files Browse the repository at this point in the history
* Java: Add command `BLMPOP` (#301)

* Java: Add command BLMPOP

---------

Co-authored-by: TJ Zhang <tj.zhang@improving.com>

* doc updates

---------

Co-authored-by: TJ Zhang <tj.zhang@improving.com>
  • Loading branch information
2 people authored and cyip10 committed Jun 24, 2024
1 parent 1d670ae commit 54901a3
Show file tree
Hide file tree
Showing 11 changed files with 322 additions and 4 deletions.
1 change: 1 addition & 0 deletions glide-core/src/client/value_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ pub(crate) fn expected_type_for_cmd(cmd: &Cmd) -> Option<ExpectedReturnType> {
b"JSON.TOGGLE" => Some(ExpectedReturnType::JsonToggleReturnType),
b"GEOPOS" => Some(ExpectedReturnType::ArrayOfArraysOfDoubleOrNull),
b"LMPOP" => Some(ExpectedReturnType::ArrayOfStringAndArrays),
b"BLMPOP" => Some(ExpectedReturnType::ArrayOfStringAndArrays),
b"HRANDFIELD" => cmd
.position(b"WITHVALUES")
.map(|_| ExpectedReturnType::ArrayOfPairs),
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 @@ -193,6 +193,7 @@ enum RequestType {
LMPop = 155;
ExpireTime = 156;
PExpireTime = 157;
BLMPop = 158;
}

message Command {
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 @@ -163,6 +163,7 @@ pub enum RequestType {
LMPop = 155,
ExpireTime = 156,
PExpireTime = 157,
BLMPop = 158,
}

fn get_two_word_command(first: &str, second: &str) -> Cmd {
Expand Down Expand Up @@ -319,6 +320,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::BZMPop => RequestType::BZMPop,
ProtobufRequestType::SetBit => RequestType::SetBit,
ProtobufRequestType::LMPop => RequestType::LMPop,
ProtobufRequestType::BLMPop => RequestType::BLMPop,
ProtobufRequestType::ZInterCard => RequestType::ZInterCard,
ProtobufRequestType::ZMPop => RequestType::ZMPop,
ProtobufRequestType::GetBit => RequestType::GetBit,
Expand Down Expand Up @@ -480,6 +482,7 @@ impl RequestType {
RequestType::BitCount => Some(cmd("BITCOUNT")),
RequestType::BZMPop => Some(cmd("BZMPOP")),
RequestType::LMPop => Some(cmd("LMPOP")),
RequestType::BLMPop => Some(cmd("BLMPOP")),
RequestType::SetBit => Some(cmd("SETBIT")),
RequestType::ZInterCard => Some(cmd("ZINTERCARD")),
RequestType::ZMPop => Some(cmd("ZMPOP")),
Expand Down
29 changes: 29 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import static glide.utils.ArrayTransformUtils.convertMapToValueKeyStringArray;
import static glide.utils.ArrayTransformUtils.mapGeoDataToArray;
import static redis_request.RedisRequestOuterClass.RequestType.Append;
import static redis_request.RedisRequestOuterClass.RequestType.BLMPop;
import static redis_request.RedisRequestOuterClass.RequestType.BLPop;
import static redis_request.RedisRequestOuterClass.RequestType.BRPop;
import static redis_request.RedisRequestOuterClass.RequestType.BZMPop;
Expand Down Expand Up @@ -1514,4 +1515,32 @@ public CompletableFuture<Map<String, String[]>> lmpop(
arguments,
response -> castMapOfArrays(handleMapOrNullResponse(response), String.class));
}

@Override
public CompletableFuture<Map<String, String[]>> blmpop(
@NonNull String[] keys, @NonNull PopDirection direction, long count, double timeout) {
String[] arguments =
concatenateArrays(
new String[] {Double.toString(timeout), Long.toString(keys.length)},
keys,
new String[] {direction.toString(), COUNT_FOR_LIST_REDIS_API, Long.toString(count)});
return commandManager.submitNewCommand(
BLMPop,
arguments,
response -> castMapOfArrays(handleMapOrNullResponse(response), String.class));
}

@Override
public CompletableFuture<Map<String, String[]>> blmpop(
@NonNull String[] keys, @NonNull PopDirection direction, double timeout) {
String[] arguments =
concatenateArrays(
new String[] {Double.toString(timeout), Long.toString(keys.length)},
keys,
new String[] {direction.toString()});
return commandManager.submitNewCommand(
BLMPop,
arguments,
response -> castMapOfArrays(handleMapOrNullResponse(response), String.class));
}
}
67 changes: 67 additions & 0 deletions java/client/src/main/java/glide/api/commands/ListBaseCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -415,4 +415,71 @@ CompletableFuture<Long> linsert(
* }</pre>
*/
CompletableFuture<Map<String, String[]>> lmpop(String[] keys, PopDirection direction);

/**
* Blocks the connection until it pops one or more elements from the first non-empty list from the
* provided <code>keys</code> <code>BLMPOP</code> is the blocking variant of {@link
* #lmpop(String[], PopDirection, long)}.
*
* @apiNote
* <ol>
* <li>When in cluster mode, all <code>keys</code> must map to the same hash slot.
* <li><code>BLMPOP</code> is a client blocking command, see <a
* href="https://github.com/aws/glide-for-redis/wiki/General-Concepts#blocking-commands">Blocking
* Commands</a> for more details and best practices.
* </ol>
*
* @since Redis 7.0 and above.
* @see <a href="https://valkey.io/commands/blmpop/">valkey.io</a> for details.
* @param keys An array of keys to lists.
* @param direction The direction based on which elements are popped from - see {@link
* PopDirection}.
* @param count The maximum number of popped elements.
* @param timeout The number of seconds to wait for a blocking operation to complete. A value of
* <code>0</code> will block indefinitely.
* @return A <code>Map</code> of <code>key</code> name mapped array of popped elements.<br>
* If no member could be popped and the timeout expired, returns <code>null</code>.
* @example
* <pre>{@code
* client.lpush("testKey", new String[] {"one", "two", "three"}).get();
* Map<String, String[]> result = client.blmpop(new String[] {"testKey"}, PopDirection.LEFT, 1L, 0.1).get();
* String[] resultValue = result.get("testKey");
* assertArrayEquals(new String[] {"three"}, resultValue);
* }</pre>
*/
CompletableFuture<Map<String, String[]>> blmpop(
String[] keys, PopDirection direction, long count, double timeout);

/**
* Blocks the connection until it pops one element from the first non-empty list from the provided
* <code>keys</code> <code>BLMPOP</code> is the blocking variant of {@link #lmpop(String[],
* PopDirection)}.
*
* @apiNote
* <ol>
* <li>When in cluster mode, all <code>keys</code> must map to the same hash slot.
* <li><code>BLMPOP</code> is a client blocking command, see <a
* href="https://github.com/aws/glide-for-redis/wiki/General-Concepts#blocking-commands">Blocking
* Commands</a> for more details and best practices.
* </ol>
*
* @since Redis 7.0 and above.
* @see <a href="https://valkey.io/commands/lmpop/">valkey.io</a> for details.
* @param keys An array of keys to lists.
* @param direction The direction based on which elements are popped from - see {@link
* PopDirection}.
* @param timeout The number of seconds to wait for a blocking operation to complete. A value of
* <code>0</code> will block indefinitely.
* @return A <code>Map</code> of <code>key</code> name mapped array of the popped element.<br>
* If no member could be popped and the timeout expired, returns <code>null</code>.
* @example
* <pre>{@code
* client.lpush("testKey", new String[] {"one", "two", "three"}).get();
* Map<String, String[]> result = client.blmpop(new String[] {"testKey"}, PopDirection.LEFT, 0.1).get();
* String[] resultValue = result.get("testKey");
* assertArrayEquals(new String[] {"three"}, resultValue);
* }</pre>
*/
CompletableFuture<Map<String, String[]>> blmpop(
String[] keys, PopDirection direction, double timeout);
}
68 changes: 68 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 @@ -14,6 +14,7 @@
import static glide.utils.ArrayTransformUtils.convertMapToValueKeyStringArray;
import static glide.utils.ArrayTransformUtils.mapGeoDataToArray;
import static redis_request.RedisRequestOuterClass.RequestType.Append;
import static redis_request.RedisRequestOuterClass.RequestType.BLMPop;
import static redis_request.RedisRequestOuterClass.RequestType.BLPop;
import static redis_request.RedisRequestOuterClass.RequestType.BRPop;
import static redis_request.RedisRequestOuterClass.RequestType.BZMPop;
Expand Down Expand Up @@ -3490,6 +3491,73 @@ public T getbit(@NonNull String key, long offset) {
return getThis();
}

/**
* Blocks the connection until it pops one or more elements from the first non-empty list from the
* provided <code>keys</code>. <code>BLMPOP</code> is the blocking variant of {@link
* #lmpop(String[], PopDirection, Long)}.
*
* @apiNote <code>BLMPOP</code> is a client blocking command, see <a
* href="https://github.com/aws/glide-for-redis/wiki/General-Concepts#blocking-commands">Blocking
* Commands</a> for more details and best practices.
* @since Redis 7.0 and above.
* @see <a href="https://valkey.io/commands/blmpop/">valkey.io</a> for details.
* @param keys The list of provided <code>key</code> names.
* @param direction The direction based on which elements are popped from - see {@link
* PopDirection}.
* @param count The maximum number of popped elements.
* @param timeout The number of seconds to wait for a blocking operation to complete. A value of
* <code>0</code> will block indefinitely.
* @return Command Response - A <code>Map</code> of <code>key</code> names arrays of popped
* elements.<br>
* If no member could be popped and the timeout expired, returns <code>null</code>.
*/
public T blmpop(
@NonNull String[] keys,
@NonNull PopDirection direction,
@NonNull Long count,
double timeout) {
ArgsArray commandArgs =
buildArgs(
concatenateArrays(
new String[] {Double.toString(timeout), Long.toString(keys.length)},
keys,
new String[] {
direction.toString(), COUNT_FOR_LIST_REDIS_API, Long.toString(count)
}));
protobufTransaction.addCommands(buildCommand(BLMPop, commandArgs));
return getThis();
}

/**
* Blocks the connection until it pops one element from the first non-empty list from the provided
* <code>keys</code>. <code>BLMPOP</code> is the blocking variant of {@link #lmpop(String[],
* PopDirection)}.
*
* @apiNote <code>BLMPOP</code> is a client blocking command, see <a
* href="https://github.com/aws/glide-for-redis/wiki/General-Concepts#blocking-commands">Blocking
* Commands</a> for more details and best practices.
* @since Redis 7.0 and above.
* @see <a href="https://valkey.io/commands/lmpop/">valkey.io</a> for details.
* @param keys The list of provided <code>key</code> names.
* @param direction The direction based on which elements are popped from - see {@link
* PopDirection}.
* @param timeout The number of seconds to wait for a blocking operation to complete. A value of
* <code>0</code> will block indefinitely.
* @return Command Response - A <code>Map</code> of <code>key</code> names arrays of popped
* elements.<br>
* If no member could be popped and the timeout expired, returns <code>null</code>.
*/
public T blmpop(@NonNull String[] keys, @NonNull PopDirection direction, double timeout) {
ArgsArray commandArgs =
buildArgs(
concatenateArrays(
new String[] {Double.toString(timeout), Long.toString(keys.length)},
keys,
new String[] {direction.toString()}));
protobufTransaction.addCommands(buildCommand(BLMPop, commandArgs));
return getThis();
}

/**
* Returns the position of the first bit matching the given <code>bit</code> value.
*
Expand Down
69 changes: 69 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static redis_request.RedisRequestOuterClass.RequestType.Append;
import static redis_request.RedisRequestOuterClass.RequestType.BLMPop;
import static redis_request.RedisRequestOuterClass.RequestType.BLPop;
import static redis_request.RedisRequestOuterClass.RequestType.BRPop;
import static redis_request.RedisRequestOuterClass.RequestType.BZMPop;
Expand Down Expand Up @@ -4819,6 +4820,74 @@ public void setbit_returns_success() {
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void blmpop_returns_success() {
// setup
String key = "testKey";
String key2 = "testKey2";
String[] keys = {key, key2};
PopDirection popDirection = PopDirection.LEFT;
double timeout = 0.1;
String[] arguments =
new String[] {Double.toString(timeout), "2", key, key2, popDirection.toString()};
Map<String, String[]> value = Map.of(key, new String[] {"five"});

CompletableFuture<Map<String, String[]>> testResponse = new CompletableFuture<>();
testResponse.complete(value);

// match on protobuf request
when(commandManager.<Map<String, String[]>>submitNewCommand(eq(BLMPop), eq(arguments), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Map<String, String[]>> response = service.blmpop(keys, popDirection, timeout);
Map<String, String[]> payload = response.get();

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

@SneakyThrows
@Test
public void blmpop_with_count_returns_success() {
// setup
String key = "testKey";
String key2 = "testKey2";
String[] keys = {key, key2};
PopDirection popDirection = PopDirection.LEFT;
long count = 1L;
double timeout = 0.1;
String[] arguments =
new String[] {
Double.toString(timeout),
"2",
key,
key2,
popDirection.toString(),
COUNT_FOR_LIST_REDIS_API,
Long.toString(count)
};
Map<String, String[]> value = Map.of(key, new String[] {"five"});

CompletableFuture<Map<String, String[]>> testResponse = new CompletableFuture<>();
testResponse.complete(value);

// match on protobuf request
when(commandManager.<Map<String, String[]>>submitNewCommand(eq(BLMPop), eq(arguments), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Map<String, String[]>> response =
service.blmpop(keys, popDirection, count, timeout);
Map<String, String[]> payload = response.get();

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

@SneakyThrows
@Test
public void getbit_returns_success() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import static glide.api.models.commands.stream.StreamTrimOptions.TRIM_MINID_REDIS_API;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static redis_request.RedisRequestOuterClass.RequestType.Append;
import static redis_request.RedisRequestOuterClass.RequestType.BLMPop;
import static redis_request.RedisRequestOuterClass.RequestType.BLPop;
import static redis_request.RedisRequestOuterClass.RequestType.BRPop;
import static redis_request.RedisRequestOuterClass.RequestType.BZMPop;
Expand Down Expand Up @@ -847,6 +848,11 @@ InfScoreBound.NEGATIVE_INFINITY, new ScoreBoundary(3, false), new Limit(1, 2)),
transaction.lmpop(new String[] {"key"}, PopDirection.LEFT, 1L);
results.add(Pair.of(LMPop, buildArgs("1", "key", "LEFT", "COUNT", "1")));

transaction.blmpop(new String[] {"key"}, PopDirection.LEFT, 0.1);
results.add(Pair.of(BLMPop, buildArgs("0.1", "1", "key", "LEFT")));
transaction.blmpop(new String[] {"key"}, PopDirection.LEFT, 1L, 0.1);
results.add(Pair.of(BLMPop, buildArgs("0.1", "1", "key", "LEFT", "COUNT", "1")));

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

for (int idx = 0; idx < protobufTransaction.getCommandsCount(); idx++) {
Expand Down
Loading

0 comments on commit 54901a3

Please sign in to comment.