Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java: Add XLEN command #1479

Merged
merged 4 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -194,6 +194,7 @@ enum RequestType {
ExpireTime = 156;
PExpireTime = 157;
BLMPop = 158;
XLen = 159;
}

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 @@ -164,6 +164,7 @@ pub enum RequestType {
ExpireTime = 156,
PExpireTime = 157,
BLMPop = 158,
XLen = 159,
}

fn get_two_word_command(first: &str, second: &str) -> Cmd {
Expand Down Expand Up @@ -331,6 +332,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::HStrlen => RequestType::HStrlen,
ProtobufRequestType::ExpireTime => RequestType::ExpireTime,
ProtobufRequestType::PExpireTime => RequestType::PExpireTime,
ProtobufRequestType::XLen => RequestType::XLen,
}
}
}
Expand Down Expand Up @@ -494,6 +496,7 @@ impl RequestType {
RequestType::HStrlen => Some(cmd("HSTRLEN")),
RequestType::ExpireTime => Some(cmd("EXPIRETIME")),
RequestType::PExpireTime => Some(cmd("PEXPIRETIME")),
RequestType::XLen => Some(cmd("XLEN")),
}
}
}
6 changes: 6 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Type;
import static redis_request.RedisRequestOuterClass.RequestType.Unlink;
import static redis_request.RedisRequestOuterClass.RequestType.XAdd;
import static redis_request.RedisRequestOuterClass.RequestType.XLen;
import static redis_request.RedisRequestOuterClass.RequestType.XTrim;
import static redis_request.RedisRequestOuterClass.RequestType.ZAdd;
import static redis_request.RedisRequestOuterClass.RequestType.ZCard;
Expand Down Expand Up @@ -1222,6 +1223,11 @@ public CompletableFuture<Long> xtrim(@NonNull String key, @NonNull StreamTrimOpt
return commandManager.submitNewCommand(XTrim, arguments, this::handleLongResponse);
}

@Override
public CompletableFuture<Long> xlen(@NonNull String key) {
return commandManager.submitNewCommand(XLen, new String[] {key}, this::handleLongResponse);
}

@Override
public CompletableFuture<Long> pttl(@NonNull String key) {
return commandManager.submitNewCommand(PTTL, new String[] {key}, this::handleLongResponse);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,19 @@ public interface StreamBaseCommands {
* }</pre>
*/
CompletableFuture<Long> xtrim(String key, StreamTrimOptions options);

/**
* Returns the number of entries in the stream stored at <code>key</code>.
*
* @see <a href="https://valkey.io/commands/xlen/">valkey.io</a> for details.
* @param key The key of the stream.
* @return The number of entries in the stream. If <code>key</code> does not exist, return <code>0
* </code>.
* @example
* <pre>{@code
* Long num = client.xlen("key").get();
* assert num == 2L; // Stream has 2 entries
* }</pre>
*/
CompletableFuture<Long> xlen(String key);
}
14 changes: 14 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 @@ -118,6 +118,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Type;
import static redis_request.RedisRequestOuterClass.RequestType.Unlink;
import static redis_request.RedisRequestOuterClass.RequestType.XAdd;
import static redis_request.RedisRequestOuterClass.RequestType.XLen;
import static redis_request.RedisRequestOuterClass.RequestType.XTrim;
import static redis_request.RedisRequestOuterClass.RequestType.ZAdd;
import static redis_request.RedisRequestOuterClass.RequestType.ZCard;
Expand Down Expand Up @@ -2615,6 +2616,19 @@ public T xtrim(@NonNull String key, @NonNull StreamTrimOptions options) {
return getThis();
}

/**
* Returns the number of entries in the stream stored at <code>key</code>.
*
* @see <a href="https://valkey.io/commands/xlen/">valkey.io</a> for details.
* @param key The key of the stream.
* @return Command Response - The number of entries in the stream. If <code>key</code> does not
* exist, return <code>0</code>.
*/
public T xlen(@NonNull String key) {
protobufTransaction.addCommands(buildCommand(XLen, buildArgs(key)));
return getThis();
}

/**
* Returns the remaining time to live of <code>key</code> that has a timeout, in milliseconds.
*
Expand Down
6 changes: 3 additions & 3 deletions java/client/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
exports glide.api.commands;
exports glide.api.models;
exports glide.api.models.commands;
exports glide.api.models.commands.stream;
exports glide.api.models.configuration;
exports glide.api.models.exceptions;
exports glide.api.models.commands.bitmap;
exports glide.api.models.commands.geospatial;
exports glide.api.models.commands.function;
exports glide.api.models.commands.stream;
exports glide.api.models.configuration;
exports glide.api.models.exceptions;

requires com.google.protobuf;
requires io.netty.codec;
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 @@ -139,6 +139,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Type;
import static redis_request.RedisRequestOuterClass.RequestType.Unlink;
import static redis_request.RedisRequestOuterClass.RequestType.XAdd;
import static redis_request.RedisRequestOuterClass.RequestType.XLen;
import static redis_request.RedisRequestOuterClass.RequestType.XTrim;
import static redis_request.RedisRequestOuterClass.RequestType.ZAdd;
import static redis_request.RedisRequestOuterClass.RequestType.ZCard;
Expand Down Expand Up @@ -3973,6 +3974,29 @@ public void xtrim_with_options_to_arguments(
assertArrayEquals(expectedArgs, options.toArgs());
}

@Test
@SneakyThrows
public void xlen_returns_success() {
// setup
String key = "testKey";
Long completedResult = 99L;

CompletableFuture<Long> testResponse = new CompletableFuture<>();
testResponse.complete(completedResult);

// match on protobuf request
when(commandManager.<Long>submitNewCommand(eq(XLen), eq(new String[] {key}), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Long> response = service.xlen(key);
Long payload = response.get();

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

@SneakyThrows
@Test
public void type_returns_success() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Type;
import static redis_request.RedisRequestOuterClass.RequestType.Unlink;
import static redis_request.RedisRequestOuterClass.RequestType.XAdd;
import static redis_request.RedisRequestOuterClass.RequestType.XLen;
import static redis_request.RedisRequestOuterClass.RequestType.XTrim;
import static redis_request.RedisRequestOuterClass.RequestType.ZAdd;
import static redis_request.RedisRequestOuterClass.RequestType.ZCard;
Expand Down Expand Up @@ -675,6 +676,9 @@ InfScoreBound.NEGATIVE_INFINITY, new ScoreBoundary(3, false), new Limit(1, 2)),
transaction.xtrim("key", new MinId(true, "id"));
results.add(Pair.of(XTrim, buildArgs("key", TRIM_MINID_REDIS_API, TRIM_EXACT_REDIS_API, "id")));

transaction.xlen("key");
results.add(Pair.of(XLen, buildArgs("key")));

transaction.time();
results.add(Pair.of(Time, buildArgs()));

Expand Down
57 changes: 10 additions & 47 deletions java/integTest/src/test/java/glide/SharedCommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -2909,7 +2909,7 @@ public void bzmpop_timeout_check(BaseClient client) {
@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
public void xadd_and_xtrim(BaseClient client) {
public void xadd_xlen_and_xtrim(BaseClient client) {
String key = UUID.randomUUID().toString();
String field1 = UUID.randomUUID().toString();
String field2 = UUID.randomUUID().toString();
Expand All @@ -2934,17 +2934,7 @@ public void xadd_and_xtrim(BaseClient client) {
.get());

assertNotNull(client.xadd(key, Map.of(field1, "foo2", field2, "bar2")).get());
// TODO update test when XLEN is available
if (client instanceof RedisClient) {
assertEquals(2L, ((RedisClient) client).customCommand(new String[] {"XLEN", key}).get());
} else if (client instanceof RedisClusterClient) {
assertEquals(
2L,
((RedisClusterClient) client)
.customCommand(new String[] {"XLEN", key})
.get()
.getSingleValue());
}
assertEquals(2L, client.xlen(key).get());

// this will trim the first entry.
String id =
Expand All @@ -2955,17 +2945,7 @@ public void xadd_and_xtrim(BaseClient client) {
StreamAddOptions.builder().trim(new MaxLen(true, 2L)).build())
.get();
assertNotNull(id);
// TODO update test when XLEN is available
if (client instanceof RedisClient) {
assertEquals(2L, ((RedisClient) client).customCommand(new String[] {"XLEN", key}).get());
} else if (client instanceof RedisClusterClient) {
assertEquals(
2L,
((RedisClusterClient) client)
.customCommand(new String[] {"XLEN", key})
.get()
.getSingleValue());
}
assertEquals(2L, client.xlen(key).get());

// this will trim the second entry.
assertNotNull(
Expand All @@ -2975,40 +2955,23 @@ public void xadd_and_xtrim(BaseClient client) {
Map.of(field1, "foo4", field2, "bar4"),
StreamAddOptions.builder().trim(new MinId(true, id)).build())
.get());
// TODO update test when XLEN is available
if (client instanceof RedisClient) {
assertEquals(2L, ((RedisClient) client).customCommand(new String[] {"XLEN", key}).get());
} else if (client instanceof RedisClusterClient) {
assertEquals(
2L,
((RedisClusterClient) client)
.customCommand(new String[] {"XLEN", key})
.get()
.getSingleValue());
}
assertEquals(2L, client.xlen(key).get());

// test xtrim to remove 1 element
assertEquals(1L, client.xtrim(key, new MaxLen(1)).get());
// TODO update test when XLEN is available
if (client instanceof RedisClient) {
assertEquals(1L, ((RedisClient) client).customCommand(new String[] {"XLEN", key}).get());
} else if (client instanceof RedisClusterClient) {
assertEquals(
1L,
((RedisClusterClient) client)
.customCommand(new String[] {"XLEN", key})
.get()
.getSingleValue());
}
assertEquals(1L, client.xlen(key).get());

// Key does not exist - returns 0
assertEquals(0L, client.xtrim(key, new MaxLen(true, 1)).get());
assertEquals(0L, client.xtrim(key2, new MaxLen(true, 1)).get());
assertEquals(0L, client.xlen(key2).get());

// Key exists, but it is not a stream
// Throw Exception: Key exists - but it is not a stream
assertEquals(OK, client.set(key2, "xtrimtest").get());
ExecutionException executionException =
assertThrows(ExecutionException.class, () -> client.xtrim(key2, new MinId("0-1")).get());
assertTrue(executionException.getCause() instanceof RequestException);
executionException = assertThrows(ExecutionException.class, () -> client.xlen(key2).get());
assertTrue(executionException.getCause() instanceof RequestException);
}

@SneakyThrows
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -530,12 +530,14 @@ private static Object[] streamCommands(BaseTransaction<?> transaction) {
.xadd(streamKey1, Map.of("field1", "value1"), StreamAddOptions.builder().id("0-1").build())
.xadd(streamKey1, Map.of("field2", "value2"), StreamAddOptions.builder().id("0-2").build())
.xadd(streamKey1, Map.of("field3", "value3"), StreamAddOptions.builder().id("0-3").build())
.xlen(streamKey1)
.xtrim(streamKey1, new MinId(true, "0-2"));

return new Object[] {
"0-1", // xadd(streamKey1, Map.of("field1", "value1"), ... .id("0-1").build());
"0-2", // xadd(streamKey1, Map.of("field2", "value2"), ... .id("0-2").build());
"0-3", // xadd(streamKey1, Map.of("field3", "value3"), ... .id("0-3").build());
3L, // xlen(streamKey1)
1L, // xtrim(streamKey1, new MinId(true, "0-2"))
};
}
Expand Down
Loading