Skip to content

Commit 345e0f9

Browse files
authored
Json commands not exposed in AsyncCluster #3048 (#3049)
* Added missing ASYNC and REACTIVE interfaces for the cluster commands * Handle container start failures more gracefully * Attempt to start the containers three times before failing
1 parent 39682dc commit 345e0f9

File tree

5 files changed

+72
-14
lines changed

5 files changed

+72
-14
lines changed

src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@
3737
* @author dengliming
3838
* @since 4.0
3939
*/
40-
public interface RedisClusterAsyncCommands<K, V> extends BaseRedisAsyncCommands<K, V>, RedisAclAsyncCommands<K, V>,
41-
RedisFunctionAsyncCommands<K, V>, RedisGeoAsyncCommands<K, V>, RedisHashAsyncCommands<K, V>,
42-
RedisHLLAsyncCommands<K, V>, RedisKeyAsyncCommands<K, V>, RedisListAsyncCommands<K, V>,
43-
RedisScriptingAsyncCommands<K, V>, RedisServerAsyncCommands<K, V>, RedisSetAsyncCommands<K, V>,
44-
RedisSortedSetAsyncCommands<K, V>, RedisStreamAsyncCommands<K, V>, RedisStringAsyncCommands<K, V> {
40+
public interface RedisClusterAsyncCommands<K, V>
41+
extends BaseRedisAsyncCommands<K, V>, RedisAclAsyncCommands<K, V>, RedisFunctionAsyncCommands<K, V>,
42+
RedisGeoAsyncCommands<K, V>, RedisHashAsyncCommands<K, V>, RedisHLLAsyncCommands<K, V>, RedisKeyAsyncCommands<K, V>,
43+
RedisListAsyncCommands<K, V>, RedisScriptingAsyncCommands<K, V>, RedisServerAsyncCommands<K, V>,
44+
RedisSetAsyncCommands<K, V>, RedisSortedSetAsyncCommands<K, V>, RedisStreamAsyncCommands<K, V>,
45+
RedisStringAsyncCommands<K, V>, RedisJsonAsyncCommands<K, V> {
4546

4647
/**
4748
* Set the default timeout for operations. A zero timeout value indicates to not time out.

src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@
3737
* @author dengliming
3838
* @since 5.0
3939
*/
40-
public interface RedisClusterReactiveCommands<K, V> extends BaseRedisReactiveCommands<K, V>, RedisAclReactiveCommands<K, V>,
41-
RedisFunctionReactiveCommands<K, V>, RedisGeoReactiveCommands<K, V>, RedisHashReactiveCommands<K, V>,
42-
RedisHLLReactiveCommands<K, V>, RedisKeyReactiveCommands<K, V>, RedisListReactiveCommands<K, V>,
43-
RedisScriptingReactiveCommands<K, V>, RedisServerReactiveCommands<K, V>, RedisSetReactiveCommands<K, V>,
44-
RedisSortedSetReactiveCommands<K, V>, RedisStreamReactiveCommands<K, V>, RedisStringReactiveCommands<K, V> {
40+
public interface RedisClusterReactiveCommands<K, V>
41+
extends BaseRedisReactiveCommands<K, V>, RedisAclReactiveCommands<K, V>, RedisFunctionReactiveCommands<K, V>,
42+
RedisGeoReactiveCommands<K, V>, RedisHashReactiveCommands<K, V>, RedisHLLReactiveCommands<K, V>,
43+
RedisKeyReactiveCommands<K, V>, RedisListReactiveCommands<K, V>, RedisScriptingReactiveCommands<K, V>,
44+
RedisServerReactiveCommands<K, V>, RedisSetReactiveCommands<K, V>, RedisSortedSetReactiveCommands<K, V>,
45+
RedisStreamReactiveCommands<K, V>, RedisStringReactiveCommands<K, V>, RedisJsonReactiveCommands<K, V> {
4546

4647
/**
4748
* Set the default timeout for operations. A zero timeout value indicates to not time out.

src/test/java/io/lettuce/core/RedisContainerIntegrationTests.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import org.testcontainers.junit.jupiter.Testcontainers;
1717

1818
import java.io.File;
19-
import java.io.IOException;
2019

2120
@Testcontainers
2221
public class RedisContainerIntegrationTests {
@@ -27,22 +26,41 @@ public class RedisContainerIntegrationTests {
2726

2827
private static final String REDIS_STACK_CLUSTER = "clustered-stack";
2928

29+
private static Exception initializationException;
30+
3031
public static ComposeContainer CLUSTERED_STACK = new ComposeContainer(
3132
new File("src/test/resources/docker/docker-compose.yml")).withExposedService(REDIS_STACK_CLUSTER, 36379)
3233
.withExposedService(REDIS_STACK_CLUSTER, 36380).withExposedService(REDIS_STACK_CLUSTER, 36381)
3334
.withExposedService(REDIS_STACK_CLUSTER, 36382).withExposedService(REDIS_STACK_CLUSTER, 36383)
3435
.withExposedService(REDIS_STACK_CLUSTER, 36384).withExposedService(REDIS_STACK_STANDALONE, 6379)
3536
.withLocalCompose(true);
3637

37-
@BeforeAll
38-
public static void setup() throws IOException, InterruptedException {
38+
// Singleton container pattern - start the containers only once
39+
// See https://java.testcontainers.org/test_framework_integration/manual_lifecycle_control/#singleton-containers
40+
static {
41+
int attempts = 0;
42+
3943
// In case you need to debug the container uncomment these lines to redirect the output
4044
CLUSTERED_STACK.withLogConsumer(REDIS_STACK_CLUSTER, (OutputFrame frame) -> LOGGER.debug(frame.getUtf8String()));
4145
CLUSTERED_STACK.withLogConsumer(REDIS_STACK_STANDALONE, (OutputFrame frame) -> LOGGER.debug(frame.getUtf8String()));
4246

4347
CLUSTERED_STACK.waitingFor(REDIS_STACK_CLUSTER,
4448
Wait.forLogMessage(".*Background RDB transfer terminated with success.*", 1));
45-
CLUSTERED_STACK.start();
49+
do {
50+
try {
51+
CLUSTERED_STACK.start();
52+
} catch (Exception e) {
53+
initializationException = e;
54+
}
55+
// Attempt to stabilize the pipeline - sometime the `docker compose up` fails randomly
56+
} while (initializationException != null && attempts++ < 3);
57+
}
58+
59+
@BeforeAll
60+
public static void checkContainerInitialization() {
61+
if (initializationException != null) {
62+
throw new IllegalStateException("Failed to initialize containers", initializationException);
63+
}
4664
}
4765

4866
}

src/test/java/io/lettuce/core/json/RedisJsonClusterIntegrationTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import io.lettuce.core.RedisContainerIntegrationTests;
1111
import io.lettuce.core.RedisURI;
1212
import io.lettuce.core.cluster.RedisClusterClient;
13+
import io.lettuce.core.cluster.api.async.RedisClusterAsyncCommands;
14+
import io.lettuce.core.cluster.api.reactive.RedisClusterReactiveCommands;
1315
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
1416
import io.lettuce.core.json.arguments.JsonGetArgs;
1517
import io.lettuce.core.json.arguments.JsonMsetArgs;
@@ -21,6 +23,7 @@
2123
import org.junit.jupiter.api.Test;
2224
import org.junit.jupiter.params.ParameterizedTest;
2325
import org.junit.jupiter.params.provider.ValueSource;
26+
import reactor.test.StepVerifier;
2427

2528
import java.io.IOException;
2629
import java.nio.file.Files;
@@ -30,6 +33,7 @@
3033
import java.time.format.DateTimeFormatter;
3134
import java.util.Arrays;
3235
import java.util.List;
36+
import java.util.concurrent.ExecutionException;
3337

3438
import static io.lettuce.TestTags.INTEGRATION_TEST;
3539
import static org.assertj.core.api.Assertions.assertThat;
@@ -114,6 +118,22 @@ void jsonArrinsert(String path) {
114118
assertThat(arrayIndex.get(0).longValue()).isEqualTo(3L);
115119
}
116120

121+
@Test
122+
void jsonArrLenAsyncAndReactive() throws ExecutionException, InterruptedException {
123+
RedisClusterAsyncCommands<String, String> asyncCommands = client.connect().async();
124+
RedisClusterReactiveCommands<String, String> reactiveCommands = client.connect().reactive();
125+
126+
JsonPath myPath = JsonPath.of(MOUNTAIN_BIKES_V1);
127+
128+
List<Long> poppedJson = asyncCommands.jsonArrlen(BIKES_INVENTORY, myPath).get();
129+
assertThat(poppedJson).hasSize(1);
130+
assertThat(poppedJson.get(0).longValue()).isEqualTo(3);
131+
132+
StepVerifier.create(reactiveCommands.jsonArrlen(BIKES_INVENTORY, myPath)).consumeNextWith(actual -> {
133+
assertThat(actual).isEqualTo(3);
134+
}).verifyComplete();
135+
}
136+
117137
@ParameterizedTest(name = "With {0} as path")
118138
@ValueSource(strings = { MOUNTAIN_BIKES_V1, MOUNTAIN_BIKES_V2 })
119139
void jsonArrlen(String path) {

src/test/java/io/lettuce/core/json/RedisJsonIntegrationTests.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.lettuce.core.RedisURI;
1515
import io.lettuce.core.api.StatefulRedisConnection;
1616
import io.lettuce.core.api.async.RedisAsyncCommands;
17+
import io.lettuce.core.api.reactive.RedisReactiveCommands;
1718
import io.lettuce.core.api.sync.RedisCommands;
1819
import io.lettuce.core.codec.ByteArrayCodec;
1920
import io.lettuce.core.codec.StringCodec;
@@ -26,6 +27,7 @@
2627
import org.junit.jupiter.params.ParameterizedTest;
2728
import org.junit.jupiter.params.provider.ValueSource;
2829
import reactor.core.publisher.Mono;
30+
import reactor.test.StepVerifier;
2931

3032
import java.io.IOException;
3133
import java.nio.ByteBuffer;
@@ -130,6 +132,22 @@ void jsonArrlen(String path) {
130132
assertThat(poppedJson.get(0).longValue()).isEqualTo(3);
131133
}
132134

135+
@Test
136+
void jsonArrLenAsyncAndReactive() throws ExecutionException, InterruptedException {
137+
RedisAsyncCommands<String, String> asyncCommands = client.connect().async();
138+
RedisReactiveCommands<String, String> reactiveCommands = client.connect().reactive();
139+
140+
JsonPath myPath = JsonPath.of(MOUNTAIN_BIKES_V1);
141+
142+
List<Long> poppedJson = asyncCommands.jsonArrlen(BIKES_INVENTORY, myPath).get();
143+
assertThat(poppedJson).hasSize(1);
144+
assertThat(poppedJson.get(0).longValue()).isEqualTo(3);
145+
146+
StepVerifier.create(reactiveCommands.jsonArrlen(BIKES_INVENTORY, myPath)).consumeNextWith(actual -> {
147+
assertThat(actual).isEqualTo(3);
148+
}).verifyComplete();
149+
}
150+
133151
@ParameterizedTest(name = "With {0} as path")
134152
@ValueSource(strings = { MOUNTAIN_BIKES_V1, MOUNTAIN_BIKES_V2 })
135153
void jsonArrpop(String path) {

0 commit comments

Comments
 (0)