From ac663ce2b5af7bf6dac9ac9ea0fc7f37a174536f Mon Sep 17 00:00:00 2001 From: tjzhang-BQ <111323543+tjzhang-BQ@users.noreply.github.com> Date: Fri, 24 May 2024 17:30:35 -0700 Subject: [PATCH 1/2] Java: Add command `BLMPOP` (#301) * Java: Add command BLMPOP --------- Co-authored-by: TJ Zhang --- glide-core/src/client/value_conversion.rs | 1 + glide-core/src/protobuf/redis_request.proto | 1 + glide-core/src/request_type.rs | 3 + .../src/main/java/glide/api/BaseClient.java | 29 ++++++++ .../glide/api/commands/ListBaseCommands.java | 67 ++++++++++++++++++ .../glide/api/models/BaseTransaction.java | 66 ++++++++++++++++++ .../test/java/glide/api/RedisClientTest.java | 69 +++++++++++++++++++ .../glide/api/models/TransactionTests.java | 6 ++ .../test/java/glide/SharedCommandTests.java | 66 ++++++++++++++++++ .../java/glide/TransactionTestUtilities.java | 10 ++- .../test/java/glide/cluster/CommandTests.java | 6 +- 11 files changed, 320 insertions(+), 4 deletions(-) diff --git a/glide-core/src/client/value_conversion.rs b/glide-core/src/client/value_conversion.rs index c68e3a0dd1..8b3569015e 100644 --- a/glide-core/src/client/value_conversion.rs +++ b/glide-core/src/client/value_conversion.rs @@ -542,6 +542,7 @@ pub(crate) fn expected_type_for_cmd(cmd: &Cmd) -> Option { 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), diff --git a/glide-core/src/protobuf/redis_request.proto b/glide-core/src/protobuf/redis_request.proto index 2cf67e4f9d..a2ef588281 100644 --- a/glide-core/src/protobuf/redis_request.proto +++ b/glide-core/src/protobuf/redis_request.proto @@ -193,6 +193,7 @@ enum RequestType { LMPop = 155; ExpireTime = 156; PExpireTime = 157; + BLMPop = 158; } message Command { diff --git a/glide-core/src/request_type.rs b/glide-core/src/request_type.rs index d58ab19e38..be4befae0b 100644 --- a/glide-core/src/request_type.rs +++ b/glide-core/src/request_type.rs @@ -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 { @@ -319,6 +320,7 @@ impl From<::protobuf::EnumOrUnknown> 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, @@ -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")), diff --git a/java/client/src/main/java/glide/api/BaseClient.java b/java/client/src/main/java/glide/api/BaseClient.java index e1f0cf94fe..2fb2ead8e0 100644 --- a/java/client/src/main/java/glide/api/BaseClient.java +++ b/java/client/src/main/java/glide/api/BaseClient.java @@ -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; @@ -1514,4 +1515,32 @@ public CompletableFuture> lmpop( arguments, response -> castMapOfArrays(handleMapOrNullResponse(response), String.class)); } + + @Override + public CompletableFuture> 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> 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)); + } } diff --git a/java/client/src/main/java/glide/api/commands/ListBaseCommands.java b/java/client/src/main/java/glide/api/commands/ListBaseCommands.java index 66bd5c39ba..5f6f352492 100644 --- a/java/client/src/main/java/glide/api/commands/ListBaseCommands.java +++ b/java/client/src/main/java/glide/api/commands/ListBaseCommands.java @@ -415,4 +415,71 @@ CompletableFuture linsert( * } */ CompletableFuture> lmpop(String[] keys, PopDirection direction); + + /** + * Blocks the connection until it pops one or more elements from the first non-empty list from the + * provided keys BLMPOP is the blocking variant of LMPOP + * . + * + * @apiNote + *
    + *
  1. When in cluster mode, all keys must map to the same hash slot. + *
  2. BLMPOP is a client blocking command, see Blocking + * Commands for more details and best practices. + *
+ * + * @since Redis 7.0 and above. + * @see valkey.io 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 + * 0 will block indefinitely. + * @return A Map of key name mapped array of popped elements.
+ * If no member could be popped and the timeout expired, returns null. + * @example + *
{@code
+     * client.lpush("testKey", new String[] {"one", "two", "three"}).get();
+     * Map result = client.blmpop(new String[] {"testKey"}, PopDirection.LEFT, 1L, 0.1).get();
+     * String[] resultValue = result.get("testKey");
+     * assertArrayEquals(new String[] {"three"}, resultValue);
+     * }
+ */ + CompletableFuture> blmpop( + String[] keys, PopDirection direction, long count, double timeout); + + /** + * Blocks the connection until it pop one element from the first non-empty list from the provided + * keys BLMPOP is the blocking variant of LMPOP + * . + * + * @apiNote + *
    + *
  1. When in cluster mode, all keys must map to the same hash slot. + *
  2. BLMPOP is a client blocking command, see Blocking + * Commands for more details and best practices. + *
+ * + * @since Redis 7.0 and above. + * @see valkey.io 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 + * 0 will block indefinitely. + * @return A Map of key name mapped array of the popped element.
+ * If no member could be popped and the timeout expired, returns null. + * @example + *
{@code
+     * client.lpush("testKey", new String[] {"one", "two", "three"}).get();
+     * Map result = client.blmpop(new String[] {"testKey"}, PopDirection.LEFT, 0.1).get();
+     * String[] resultValue = result.get("testKey");
+     * assertArrayEquals(new String[] {"three"}, resultValue);
+     * }
+ */ + CompletableFuture> blmpop( + String[] keys, PopDirection direction, double timeout); } diff --git a/java/client/src/main/java/glide/api/models/BaseTransaction.java b/java/client/src/main/java/glide/api/models/BaseTransaction.java index 86d918ed9a..5d98e991b7 100644 --- a/java/client/src/main/java/glide/api/models/BaseTransaction.java +++ b/java/client/src/main/java/glide/api/models/BaseTransaction.java @@ -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; @@ -3490,6 +3491,71 @@ 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 keys. BLMPOP is the blocking variant of LMPOP. + * + * @apiNote BLMPOP is a client blocking command, see Blocking + * Commands for more details and best practices. + * @since Redis 7.0 and above. + * @see valkey.io for details. + * @param keys The list of provided key 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 + * 0 will block indefinitely. + * @return Command Response - A Map of key names arrays of popped + * elements.
+ * If no member could be popped and the timeout expired, returns null. + */ + 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 + * keys. BLMPOP is the blocking variant of LMPOP. + * + * @apiNote BLMPOP is a client blocking command, see Blocking + * Commands for more details and best practices. + * @since Redis 7.0 and above. + * @see valkey.io for details. + * @param keys The list of provided key 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 + * 0 will block indefinitely. + * @return Command Response - A Map of key names arrays of popped + * elements.
+ * If no member could be popped and the timeout expired, returns null. + */ + 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 bit value. * diff --git a/java/client/src/test/java/glide/api/RedisClientTest.java b/java/client/src/test/java/glide/api/RedisClientTest.java index 530202e2df..c864cf4138 100644 --- a/java/client/src/test/java/glide/api/RedisClientTest.java +++ b/java/client/src/test/java/glide/api/RedisClientTest.java @@ -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; @@ -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 value = Map.of(key, new String[] {"five"}); + + CompletableFuture> testResponse = new CompletableFuture<>(); + testResponse.complete(value); + + // match on protobuf request + when(commandManager.>submitNewCommand(eq(BLMPop), eq(arguments), any())) + .thenReturn(testResponse); + + // exercise + CompletableFuture> response = service.blmpop(keys, popDirection, timeout); + Map 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 value = Map.of(key, new String[] {"five"}); + + CompletableFuture> testResponse = new CompletableFuture<>(); + testResponse.complete(value); + + // match on protobuf request + when(commandManager.>submitNewCommand(eq(BLMPop), eq(arguments), any())) + .thenReturn(testResponse); + + // exercise + CompletableFuture> response = + service.blmpop(keys, popDirection, count, timeout); + Map payload = response.get(); + + // verify + assertEquals(testResponse, response); + assertEquals(value, payload); + } + @SneakyThrows @Test public void getbit_returns_success() { diff --git a/java/client/src/test/java/glide/api/models/TransactionTests.java b/java/client/src/test/java/glide/api/models/TransactionTests.java index 68a6d9409d..53a3dca6fe 100644 --- a/java/client/src/test/java/glide/api/models/TransactionTests.java +++ b/java/client/src/test/java/glide/api/models/TransactionTests.java @@ -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; @@ -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++) { diff --git a/java/integTest/src/test/java/glide/SharedCommandTests.java b/java/integTest/src/test/java/glide/SharedCommandTests.java index 11b770f0dc..2b39e51a40 100644 --- a/java/integTest/src/test/java/glide/SharedCommandTests.java +++ b/java/integTest/src/test/java/glide/SharedCommandTests.java @@ -4141,4 +4141,70 @@ public void lmpop(BaseClient client) { () -> client.lmpop(new String[] {nonListKey}, PopDirection.LEFT).get()); assertInstanceOf(RequestException.class, executionException.getCause()); } + + @SneakyThrows + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("getClients") + public void blmpop(BaseClient client) { + assumeTrue(REDIS_VERSION.isGreaterThanOrEqualTo("7.0.0"), "This feature added in redis 7"); + // setup + String key1 = "{key}-1" + UUID.randomUUID(); + String key2 = "{key}-2" + UUID.randomUUID(); + String nonListKey = "{key}-3" + UUID.randomUUID(); + String[] singleKeyArray = {key1}; + String[] multiKeyArray = {key2, key1}; + long count = 1L; + Long arraySize = 5L; + String[] lpushArgs = {"one", "two", "three", "four", "five"}; + Map expected = Map.of(key1, new String[] {"five"}); + Map expected2 = Map.of(key2, new String[] {"one", "two"}); + + // nothing to be popped + assertNull(client.blmpop(singleKeyArray, PopDirection.LEFT, 0.1).get()); + assertNull(client.blmpop(singleKeyArray, PopDirection.LEFT, count, 0.1).get()); + + // pushing to the arrays to be popped + assertEquals(arraySize, client.lpush(key1, lpushArgs).get()); + assertEquals(arraySize, client.lpush(key2, lpushArgs).get()); + + // assert correct result from popping + Map result = client.blmpop(singleKeyArray, PopDirection.LEFT, 0.1).get(); + assertDeepEquals(result, expected); + + // assert popping multiple elements from the right + Map result2 = client.blmpop(multiKeyArray, PopDirection.RIGHT, 2L, 0.1).get(); + assertDeepEquals(result2, expected2); + + // key exists but is not a list type key + assertEquals(OK, client.set(nonListKey, "blmpop").get()); + ExecutionException executionException = + assertThrows( + ExecutionException.class, + () -> client.blmpop(new String[] {nonListKey}, PopDirection.LEFT, 0.1).get()); + assertInstanceOf(RequestException.class, executionException.getCause()); + } + + @SneakyThrows + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("getClients") + public void blmpop_timeout_check(BaseClient client) { + assumeTrue(REDIS_VERSION.isGreaterThanOrEqualTo("7.0.0"), "This feature added in redis 7"); + String key = UUID.randomUUID().toString(); + // create new client with default request timeout (250 millis) + try (var testClient = + client instanceof RedisClient + ? RedisClient.CreateClient(commonClientConfig().build()).get() + : RedisClusterClient.CreateClient(commonClusterClientConfig().build()).get()) { + + // ensure that commands doesn't time out even if timeout > request timeout + assertNull(testClient.blmpop(new String[] {key}, PopDirection.LEFT, 1).get()); + + // with 0 timeout (no timeout) should never time out, + // but we wrap the test with timeout to avoid test failing or stuck forever + assertThrows( + TimeoutException.class, // <- future timeout, not command timeout + () -> + testClient.blmpop(new String[] {key}, PopDirection.LEFT, 0).get(3, TimeUnit.SECONDS)); + } + } } diff --git a/java/integTest/src/test/java/glide/TransactionTestUtilities.java b/java/integTest/src/test/java/glide/TransactionTestUtilities.java index be63d1b106..f7dddad6d7 100644 --- a/java/integTest/src/test/java/glide/TransactionTestUtilities.java +++ b/java/integTest/src/test/java/glide/TransactionTestUtilities.java @@ -272,9 +272,11 @@ private static Object[] listCommands(BaseTransaction transaction) { if (REDIS_VERSION.isGreaterThanOrEqualTo("7.0.0")) { transaction - .lpush(listKey4, new String[] {value1, value2, value3}) + .lpush(listKey4, new String[] {value1, value2, value3, value1, value2, value3}) .lmpop(new String[] {listKey4}, PopDirection.LEFT) - .lmpop(new String[] {listKey4}, PopDirection.LEFT, 2L); + .lmpop(new String[] {listKey4}, PopDirection.LEFT, 2L) + .blmpop(new String[] {listKey4}, PopDirection.LEFT, 0.1) + .blmpop(new String[] {listKey4}, PopDirection.LEFT, 2L, 0.1); } // listKey4 is now empty var expectedResults = @@ -302,9 +304,11 @@ private static Object[] listCommands(BaseTransaction transaction) { return concatenateArrays( expectedResults, new Object[] { - 3L, // lpush(listKey4, {value1, value2, value3}) + 6L, // lpush(listKey4, {value1, value2, value3}) Map.of(listKey4, new String[] {value3}), // lmpop({listKey4}, LEFT) Map.of(listKey4, new String[] {value2, value1}), // lmpop({listKey4}, LEFT, 1L) + Map.of(listKey4, new String[] {value3}), // blmpop({listKey4}, LEFT, 0.1) + Map.of(listKey4, new String[] {value2, value1}), // blmpop(listKey4}, LEFT, 1L, 0.1) }); } diff --git a/java/integTest/src/test/java/glide/cluster/CommandTests.java b/java/integTest/src/test/java/glide/cluster/CommandTests.java index ad623e317d..cf68903d74 100644 --- a/java/integTest/src/test/java/glide/cluster/CommandTests.java +++ b/java/integTest/src/test/java/glide/cluster/CommandTests.java @@ -701,7 +701,11 @@ public static Stream callCrossSlotCommandsWhichShouldFail() { Arguments.of( "bitop", null, - clusterClient.bitop(BitwiseOperation.OR, "abc", new String[] {"zxy", "lkn"}))); + clusterClient.bitop(BitwiseOperation.OR, "abc", new String[] {"zxy", "lkn"})), + Arguments.of( + "blmpop", + "7.0.0", + clusterClient.blmpop(new String[] {"abc", "def"}, PopDirection.LEFT, 1L, 0.1))); } @SneakyThrows From f5e25359a94fbd08ba81fbc75c7a4373ad455353 Mon Sep 17 00:00:00 2001 From: TJ Zhang Date: Mon, 27 May 2024 14:35:22 -0700 Subject: [PATCH 2/2] doc updates --- .../java/glide/api/commands/ListBaseCommands.java | 14 +++++++------- .../java/glide/api/models/BaseTransaction.java | 10 ++++++---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/java/client/src/main/java/glide/api/commands/ListBaseCommands.java b/java/client/src/main/java/glide/api/commands/ListBaseCommands.java index 5f6f352492..f06c64394c 100644 --- a/java/client/src/main/java/glide/api/commands/ListBaseCommands.java +++ b/java/client/src/main/java/glide/api/commands/ListBaseCommands.java @@ -418,8 +418,8 @@ CompletableFuture linsert( /** * Blocks the connection until it pops one or more elements from the first non-empty list from the - * provided keys BLMPOP is the blocking variant of LMPOP - * . + * provided keys BLMPOP is the blocking variant of {@link + * #lmpop(String[], PopDirection, long)}. * * @apiNote *
    @@ -437,7 +437,7 @@ CompletableFuture linsert( * @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 * 0 will block indefinitely. - * @return A Map of key name mapped array of popped elements.
    + * @return A Map of key name mapped array of popped elements.
    * If no member could be popped and the timeout expired, returns null. * @example *
    {@code
    @@ -451,9 +451,9 @@ CompletableFuture> blmpop(
                 String[] keys, PopDirection direction, long count, double timeout);
     
         /**
    -     * Blocks the connection until it pop one element from the first non-empty list from the provided
    -     * keys BLMPOP is the blocking variant of LMPOP
    -     * .
    +     * Blocks the connection until it pops one element from the first non-empty list from the provided
    +     * keys BLMPOP is the blocking variant of {@link #lmpop(String[],
    +     * PopDirection)}.
          *
          * @apiNote
          *     
      @@ -470,7 +470,7 @@ CompletableFuture> blmpop( * PopDirection}. * @param timeout The number of seconds to wait for a blocking operation to complete. A value of * 0 will block indefinitely. - * @return A Map of key name mapped array of the popped element.
      + * @return A Map of key name mapped array of the popped element.
      * If no member could be popped and the timeout expired, returns null. * @example *
      {@code
      diff --git a/java/client/src/main/java/glide/api/models/BaseTransaction.java b/java/client/src/main/java/glide/api/models/BaseTransaction.java
      index 5d98e991b7..f836decaa9 100644
      --- a/java/client/src/main/java/glide/api/models/BaseTransaction.java
      +++ b/java/client/src/main/java/glide/api/models/BaseTransaction.java
      @@ -3493,7 +3493,8 @@ public T getbit(@NonNull String key, long offset) {
       
           /**
            * Blocks the connection until it pops one or more elements from the first non-empty list from the
      -     * provided keys. BLMPOP is the blocking variant of LMPOP.
      +     * provided keys. BLMPOP is the blocking variant of {@link
      +     * #lmpop(String[], PopDirection, Long)}.
            *
            * @apiNote BLMPOP is a client blocking command, see Blocking
      @@ -3507,7 +3508,7 @@ public T getbit(@NonNull String key, long offset) {
            * @param timeout The number of seconds to wait for a blocking operation to complete. A value of
            *     0 will block indefinitely.
            * @return Command Response - A Map of key names arrays of popped
      -     *     elements. 
      + * elements.
      * If no member could be popped and the timeout expired, returns null. */ public T blmpop( @@ -3529,7 +3530,8 @@ public T blmpop( /** * Blocks the connection until it pops one element from the first non-empty list from the provided - * keys. BLMPOP is the blocking variant of LMPOP. + * keys. BLMPOP is the blocking variant of {@link #lmpop(String[], + * PopDirection)}. * * @apiNote BLMPOP is a client blocking command, see
      Blocking @@ -3542,7 +3544,7 @@ public T blmpop( * @param timeout The number of seconds to wait for a blocking operation to complete. A value of * 0 will block indefinitely. * @return Command Response - A Map of key names arrays of popped - * elements.
      + * elements.
      * If no member could be popped and the timeout expired, returns null. */ public T blmpop(@NonNull String[] keys, @NonNull PopDirection direction, double timeout) {