actions = Collections.synchronizedList(new ArrayList<>());
71 |
72 | metrics = new ClientMetrics() {
73 | @Override
74 | public Object requestBegin(String uri, Object request) {
75 | actions.add("requestBegin");
76 | return metric;
77 | }
78 |
79 | @Override
80 | public void requestEnd(Object requestMetric, long bytesWritten) {
81 | test.assertTrue(requestMetric == metric);
82 | actions.add("requestEnd");
83 | }
84 |
85 | @Override
86 | public void responseBegin(Object requestMetric, Object response) {
87 | test.assertTrue(requestMetric == metric);
88 | actions.add("responseBegin");
89 | }
90 |
91 | @Override
92 | public void responseEnd(Object requestMetric, long bytesRead) {
93 | test.assertTrue(requestMetric == metric);
94 | actions.add("responseEnd");
95 | }
96 |
97 | @Override
98 | public void requestReset(Object requestMetric) {
99 | test.assertTrue(requestMetric == metric);
100 | actions.add("fail");
101 | }
102 | };
103 |
104 | vertx.runOnContext(ignored -> {
105 | client.send(request).onComplete(result -> {
106 | if (success) {
107 | test.assertTrue(result.succeeded());
108 | test.assertEquals(Arrays.asList("requestBegin", "requestEnd", "responseBegin", "responseEnd"), actions);
109 | } else {
110 | test.assertTrue(result.failed());
111 | test.assertEquals(Arrays.asList("requestBegin", "requestEnd", "fail"), actions);
112 | }
113 | test.assertEquals("the-namespace", reportedNamespace);
114 | async.complete();
115 | });
116 | });
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisClientPikaSecureTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.core.json.JsonObject;
4 | import io.vertx.ext.unit.Async;
5 | import io.vertx.ext.unit.TestContext;
6 | import io.vertx.ext.unit.junit.RunTestOnContext;
7 | import io.vertx.ext.unit.junit.VertxUnitRunner;
8 | import io.vertx.redis.client.Redis;
9 | import io.vertx.redis.client.RedisOptions;
10 | import org.junit.After;
11 | import org.junit.Before;
12 | import org.junit.ClassRule;
13 | import org.junit.Rule;
14 | import org.junit.Test;
15 | import org.junit.runner.RunWith;
16 | import org.testcontainers.containers.BindMode;
17 | import org.testcontainers.containers.GenericContainer;
18 |
19 | import static io.vertx.redis.client.Command.GET;
20 | import static io.vertx.redis.client.Command.HGETALL;
21 | import static io.vertx.redis.client.Command.HSET;
22 | import static io.vertx.redis.client.Command.SET;
23 | import static io.vertx.redis.client.Request.cmd;
24 | import static io.vertx.tests.redis.client.TestUtils.randomKey;
25 |
26 | @RunWith(VertxUnitRunner.class)
27 | public class RedisClientPikaSecureTest {
28 |
29 | @Rule
30 | public final RunTestOnContext rule = new RunTestOnContext();
31 |
32 | @ClassRule
33 | public static final GenericContainer> redis = new GenericContainer<>("pikadb/pika:latest")
34 | .withClasspathResourceMapping("pika.conf", "/pika/conf/pika.conf", BindMode.READ_ONLY)
35 | .withExposedPorts(9221);
36 |
37 | private Redis client;
38 |
39 | @Before
40 | public void before(TestContext should) {
41 | final Async before = should.async();
42 |
43 | client = Redis.createClient(
44 | rule.vertx(),
45 | new RedisOptions().setConnectionString("redis://:userpass@" + redis.getHost() + ":" + redis.getFirstMappedPort()));
46 |
47 | client.connect().onComplete(onConnect -> {
48 | should.assertTrue(onConnect.succeeded());
49 | before.complete();
50 | });
51 | }
52 |
53 | @After
54 | public void after() {
55 | client.close();
56 | }
57 |
58 | @Test(timeout = 10_000L)
59 | public void testBasicInterop(TestContext should) {
60 | final Async test = should.async();
61 | final String nonexisting = randomKey();
62 | final String mykey = randomKey();
63 |
64 | client.send(cmd(GET).arg(nonexisting)).onComplete(reply0 -> {
65 | should.assertTrue(reply0.succeeded());
66 | should.assertNull(reply0.result());
67 |
68 | client.send(cmd(SET).arg(mykey).arg("Hello")).onComplete(reply1 -> {
69 | should.assertTrue(reply1.succeeded());
70 | client.send(cmd(GET).arg(mykey)).onComplete(reply2 -> {
71 | should.assertTrue(reply2.succeeded());
72 | should.assertEquals("Hello", reply2.result().toString());
73 | test.complete();
74 | });
75 | });
76 | });
77 | }
78 |
79 | @Test(timeout = 10_000L)
80 | public void testJson(TestContext should) {
81 | final Async test = should.async();
82 | final String mykey = randomKey();
83 |
84 | JsonObject json = new JsonObject()
85 | .put("key", "value");
86 |
87 | client.send(cmd(HSET).arg(mykey).arg(json))
88 | .compose(ignored -> client.send(cmd(HGETALL).arg(mykey)))
89 | .onComplete(should.asyncAssertSuccess(value -> {
90 | should.assertEquals("value", value.get("key").toString());
91 | test.complete();
92 | }));
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisClientPikaTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.core.json.JsonObject;
4 | import io.vertx.ext.unit.Async;
5 | import io.vertx.ext.unit.TestContext;
6 | import io.vertx.ext.unit.junit.RunTestOnContext;
7 | import io.vertx.ext.unit.junit.VertxUnitRunner;
8 | import io.vertx.redis.client.Redis;
9 | import io.vertx.redis.client.RedisOptions;
10 | import org.junit.After;
11 | import org.junit.Before;
12 | import org.junit.ClassRule;
13 | import org.junit.Rule;
14 | import org.junit.Test;
15 | import org.junit.runner.RunWith;
16 | import org.testcontainers.containers.GenericContainer;
17 |
18 | import static io.vertx.redis.client.Command.GET;
19 | import static io.vertx.redis.client.Command.HGETALL;
20 | import static io.vertx.redis.client.Command.HSET;
21 | import static io.vertx.redis.client.Command.SET;
22 | import static io.vertx.redis.client.Request.cmd;
23 | import static io.vertx.tests.redis.client.TestUtils.randomKey;
24 |
25 | @RunWith(VertxUnitRunner.class)
26 | public class RedisClientPikaTest {
27 |
28 | @Rule
29 | public final RunTestOnContext rule = new RunTestOnContext();
30 |
31 | @ClassRule
32 | public static final GenericContainer> redis = new GenericContainer<>("pikadb/pika:latest")
33 | .withExposedPorts(9221);
34 |
35 | private Redis client;
36 |
37 | @Before
38 | public void before(TestContext should) {
39 | final Async before = should.async();
40 |
41 | client = Redis.createClient(
42 | rule.vertx(),
43 | new RedisOptions().setConnectionString("redis://" + redis.getHost() + ":" + redis.getFirstMappedPort()));
44 |
45 | client.connect().onComplete(onConnect -> {
46 | should.assertTrue(onConnect.succeeded());
47 | before.complete();
48 | });
49 | }
50 |
51 | @After
52 | public void after() {
53 | client.close();
54 | }
55 |
56 | @Test(timeout = 10_000L)
57 | public void testBasicInterop(TestContext should) {
58 | final Async test = should.async();
59 | final String nonexisting = randomKey();
60 | final String mykey = randomKey();
61 |
62 | client.send(cmd(GET).arg(nonexisting)).onComplete(reply0 -> {
63 | should.assertTrue(reply0.succeeded());
64 | should.assertNull(reply0.result());
65 |
66 | client.send(cmd(SET).arg(mykey).arg("Hello")).onComplete(reply1 -> {
67 | should.assertTrue(reply1.succeeded());
68 | client.send(cmd(GET).arg(mykey)).onComplete(reply2 -> {
69 | should.assertTrue(reply2.succeeded());
70 | should.assertEquals("Hello", reply2.result().toString());
71 | test.complete();
72 | });
73 | });
74 | });
75 | }
76 |
77 | @Test(timeout = 10_000L)
78 | public void testJson(TestContext should) {
79 | final Async test = should.async();
80 | final String mykey = randomKey();
81 |
82 | JsonObject json = new JsonObject()
83 | .put("key", "value");
84 |
85 | client.send(cmd(HSET).arg(mykey).arg(json))
86 | .compose(ignored -> client.send(cmd(HGETALL).arg(mykey)))
87 | .onComplete(should.asyncAssertSuccess(value -> {
88 | should.assertEquals("value", value.get("key").toString());
89 | test.complete();
90 | }));
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisClientREJSONTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 | package io.vertx.tests.redis.client;
17 |
18 | import io.vertx.core.Future;
19 | import io.vertx.ext.unit.Async;
20 | import io.vertx.ext.unit.TestContext;
21 | import io.vertx.ext.unit.junit.RunTestOnContext;
22 | import io.vertx.ext.unit.junit.VertxUnitRunner;
23 | import io.vertx.redis.client.Command;
24 | import io.vertx.redis.client.Redis;
25 | import org.junit.ClassRule;
26 | import org.junit.Rule;
27 | import org.junit.Test;
28 | import org.junit.runner.RunWith;
29 | import org.testcontainers.containers.GenericContainer;
30 |
31 | import static io.vertx.redis.client.Request.cmd;
32 |
33 | /**
34 | * This test relies on a REJSON capable Redis server.
35 | */
36 | @RunWith(VertxUnitRunner.class)
37 | public class RedisClientREJSONTest {
38 |
39 | @Rule
40 | public final RunTestOnContext rule = new RunTestOnContext();
41 |
42 | @ClassRule
43 | public static final GenericContainer> container = new GenericContainer<>("redislabs/rejson:2.4.7")
44 | .withExposedPorts(6379);
45 |
46 | @Test(timeout = 30_000L)
47 | public void testJsonSetAndGet(TestContext should) {
48 | final Async test = should.async();
49 |
50 | final Redis client = Redis.createClient(
51 | rule.vertx(),
52 | "redis://" + container.getHost() + ":" + container.getFirstMappedPort());
53 |
54 | client
55 | .send(cmd(Command.create("JSON.SET")).arg("foo").arg(".").arg("\"bar\""))
56 | .compose(response -> {
57 | // OK
58 | return client.send(cmd(Command.create("JSON.GET")).arg("foo"));
59 | })
60 | .compose(response -> {
61 | should.assertEquals("\"bar\"", response.toString());
62 | return client.send(cmd(Command.create("JSON.TYPE")).arg("foo").arg("."));
63 | })
64 | .compose(response -> {
65 | should.assertEquals("string", response.toString());
66 | return Future.succeededFuture();
67 | })
68 | .onFailure(should::fail)
69 | .onSuccess(v -> {
70 | test.complete();
71 | });
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisClientTLSTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.core.buffer.Buffer;
4 | import io.vertx.core.net.JksOptions;
5 | import io.vertx.core.net.NetClientOptions;
6 | import io.vertx.ext.unit.Async;
7 | import io.vertx.ext.unit.TestContext;
8 | import io.vertx.ext.unit.junit.RunTestOnContext;
9 | import io.vertx.ext.unit.junit.VertxUnitRunner;
10 | import io.vertx.redis.client.Command;
11 | import io.vertx.redis.client.Redis;
12 | import io.vertx.redis.client.RedisOptions;
13 | import io.vertx.redis.client.Request;
14 | import io.vertx.tests.redis.containers.KeyPairAndCertificate;
15 | import io.vertx.tests.redis.containers.RedisStandalone;
16 | import org.junit.ClassRule;
17 | import org.junit.Rule;
18 | import org.junit.Test;
19 | import org.junit.runner.RunWith;
20 |
21 | @RunWith(VertxUnitRunner.class)
22 | public class RedisClientTLSTest {
23 |
24 | private static final KeyPairAndCertificate serverKey = KeyPairAndCertificate.generateSelfSigned("redis");
25 |
26 | private static final KeyPairAndCertificate clientKey = KeyPairAndCertificate.generateSelfSigned("test");
27 |
28 | private static NetClientOptions createNetClientOptions() {
29 | return new NetClientOptions()
30 | .setKeyCertOptions(new JksOptions().setValue(Buffer.buffer(clientKey.privateKeyAsJKS())).setPassword(""))
31 | .setTrustOptions(new JksOptions().setValue(Buffer.buffer(serverKey.certificateAsJKS())))
32 | .setTcpKeepAlive(true)
33 | .setTcpNoDelay(true)
34 | // we cannot know the hostname, and hostname verification is not important for this test
35 | .setHostnameVerificationAlgorithm("");
36 | }
37 |
38 | @ClassRule
39 | public static final RedisStandalone redis = RedisStandalone.builder()
40 | .enableTls(serverKey)
41 | .enableMutualTls(clientKey)
42 | .build();
43 |
44 | @Rule
45 | public final RunTestOnContext rule = new RunTestOnContext();
46 |
47 | @Test
48 | public void testConnection(TestContext should) {
49 | final Async test = should.async();
50 |
51 | Redis client = Redis.createClient(
52 | rule.vertx(),
53 | new RedisOptions()
54 | .setNetClientOptions(createNetClientOptions())
55 | .setConnectionString("rediss://" + redis.getHost() + ":" + redis.getPort()));
56 |
57 | client.connect()
58 | .onComplete(should.asyncAssertSuccess(conn -> {
59 | conn.send(Request.cmd(Command.PING))
60 | .onComplete(should.asyncAssertSuccess(ignored -> {
61 | conn.close();
62 | test.complete();
63 | }));
64 | }));
65 | }
66 |
67 | @Test
68 | public void testInvalidConnection(TestContext should) {
69 | final Async test = should.async();
70 |
71 | Redis client = Redis.createClient(
72 | rule.vertx(),
73 | new RedisOptions()
74 | .setNetClientOptions(createNetClientOptions())
75 | // in this test, Redis requires SSL and doesn't accept plain text connections;
76 | // the connection string has wrong scheme
77 | .setConnectionString("redis://" + redis.getHost() + ":" + redis.getPort()));
78 |
79 | client.connect()
80 | .onComplete(should.asyncAssertFailure(ignored -> {
81 | test.complete();
82 | }));
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisClientToBadServerTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.core.net.NetSocket;
4 | import io.vertx.ext.unit.Async;
5 | import io.vertx.ext.unit.TestContext;
6 | import io.vertx.ext.unit.junit.RunTestOnContext;
7 | import io.vertx.ext.unit.junit.VertxUnitRunner;
8 | import io.vertx.redis.client.Redis;
9 | import io.vertx.redis.client.RedisOptions;
10 | import org.junit.Rule;
11 | import org.junit.Test;
12 | import org.junit.runner.RunWith;
13 |
14 | @RunWith(VertxUnitRunner.class)
15 | public class RedisClientToBadServerTest {
16 |
17 | @Rule
18 | public final RunTestOnContext rule = new RunTestOnContext();
19 |
20 | @Test
21 | public void helloTest(TestContext should) {
22 | final Async before = should.async();
23 |
24 | rule.vertx()
25 | .createNetServer()
26 | .connectHandler(NetSocket::close)
27 | .listen(9876);
28 |
29 | Redis client = Redis.createClient(
30 | rule.vertx(),
31 | new RedisOptions().setConnectionString("redis://localhost:9876"));
32 |
33 | client.connect().onComplete(onConnect -> {
34 | should.assertFalse(onConnect.succeeded());
35 | before.complete();
36 | });
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisClusterAskTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.core.Future;
4 | import io.vertx.ext.unit.Async;
5 | import io.vertx.ext.unit.TestContext;
6 | import io.vertx.ext.unit.junit.RunTestOnContext;
7 | import io.vertx.ext.unit.junit.VertxUnitRunner;
8 | import io.vertx.redis.client.Command;
9 | import io.vertx.redis.client.Redis;
10 | import io.vertx.redis.client.RedisClientType;
11 | import io.vertx.redis.client.RedisConnection;
12 | import io.vertx.redis.client.RedisOptions;
13 | import io.vertx.redis.client.RedisReplicas;
14 | import io.vertx.redis.client.Request;
15 | import io.vertx.tests.redis.containers.RedisCluster;
16 | import org.junit.After;
17 | import org.junit.Before;
18 | import org.junit.ClassRule;
19 | import org.junit.Rule;
20 | import org.junit.Test;
21 | import org.junit.runner.RunWith;
22 |
23 | @RunWith(VertxUnitRunner.class)
24 | public class RedisClusterAskTest {
25 | @ClassRule
26 | public static final RedisCluster redis = new RedisCluster();
27 |
28 | @Rule
29 | public final RunTestOnContext rule = new RunTestOnContext();
30 |
31 | private final RedisOptions options = new RedisOptions()
32 | .setType(RedisClientType.CLUSTER)
33 | .setUseReplicas(RedisReplicas.NEVER)
34 | .addConnectionString(redis.getRedisNode0Uri())
35 | .addConnectionString(redis.getRedisNode1Uri())
36 | .addConnectionString(redis.getRedisNode2Uri())
37 | .addConnectionString(redis.getRedisNode3Uri())
38 | .addConnectionString(redis.getRedisNode4Uri())
39 | .addConnectionString(redis.getRedisNode5Uri());
40 |
41 | private Redis client;
42 | private ClusterUtils cluster;
43 |
44 | @Before
45 | public void createClient() {
46 | client = Redis.createClient(rule.vertx(), options);
47 | cluster = new ClusterUtils(rule.vertx(), client);
48 | }
49 |
50 | @After
51 | public void cleanRedis() {
52 | client.close();
53 | }
54 |
55 | @Test
56 | public void test(TestContext test) {
57 | Async async = test.async();
58 |
59 | // slot number: 16287
60 | // keys hashing to the slot: x, exs
61 | int slot = 16287;
62 | String key1 = "x";
63 | String key2 = "exs";
64 |
65 | client.connect().compose(clusterConn -> {
66 | return cluster.connectToMasterThatServesSlot(slot).compose(masterResult -> {
67 | Redis master = masterResult.redis;
68 | RedisConnection masterConn = masterResult.conn;
69 | String masterId = masterResult.id;
70 | return cluster.connectToMasterThatDoesntServeSlot(slot).compose(otherMasterResult -> {
71 | Redis otherMaster = otherMasterResult.redis;
72 | RedisConnection otherMasterConn = otherMasterResult.conn;
73 | String otherMasterId = otherMasterResult.id;
74 | return clusterConn.send(Request.cmd(Command.SET).arg(key1).arg("fubar"))
75 | .compose(ignored -> {
76 | return otherMasterConn.send(Request.cmd(Command.CLUSTER).arg("SETSLOT").arg(slot).arg("IMPORTING").arg(masterId));
77 | })
78 | .compose(ignored -> {
79 | return masterConn.send(Request.cmd(Command.CLUSTER).arg("SETSLOT").arg(slot).arg("MIGRATING").arg(otherMasterId));
80 | })
81 | .compose(ignored -> {
82 | return clusterConn.send(Request.cmd(Command.GET).arg(key1));
83 | })
84 | .compose(result -> {
85 | test.assertEquals("fubar", result.toString());
86 | return clusterConn.send(Request.cmd(Command.GET).arg(key2)); // ASK
87 | })
88 | .compose(result -> {
89 | test.assertEquals(null, result);
90 | return clusterConn.send(Request.cmd(Command.SET).arg(key2).arg("quux")); // ASK
91 | })
92 | .compose(ignored -> {
93 | return clusterConn.send(Request.cmd(Command.GET).arg(key2)); // ASK
94 | })
95 | .compose(result -> {
96 | test.assertEquals("quux", result.toString());
97 | master.close();
98 | otherMaster.close();
99 | return Future.succeededFuture();
100 | });
101 | });
102 | });
103 | }).onComplete(test.asyncAssertSuccess(ignored -> {
104 | async.complete();
105 | }));
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisClusterMovedTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.core.Future;
4 | import io.vertx.core.net.SocketAddress;
5 | import io.vertx.ext.unit.Async;
6 | import io.vertx.ext.unit.TestContext;
7 | import io.vertx.ext.unit.junit.RunTestOnContext;
8 | import io.vertx.ext.unit.junit.VertxUnitRunner;
9 | import io.vertx.redis.client.Command;
10 | import io.vertx.redis.client.Redis;
11 | import io.vertx.redis.client.RedisClientType;
12 | import io.vertx.redis.client.RedisConnection;
13 | import io.vertx.redis.client.RedisOptions;
14 | import io.vertx.redis.client.RedisReplicas;
15 | import io.vertx.redis.client.Request;
16 | import io.vertx.redis.client.impl.PooledRedisConnection;
17 | import io.vertx.tests.redis.containers.RedisCluster;
18 | import org.junit.After;
19 | import org.junit.Before;
20 | import org.junit.ClassRule;
21 | import org.junit.Rule;
22 | import org.junit.Test;
23 | import org.junit.runner.RunWith;
24 |
25 | @RunWith(VertxUnitRunner.class)
26 | public class RedisClusterMovedTest {
27 | @ClassRule
28 | public static final RedisCluster redis = new RedisCluster();
29 |
30 | @Rule
31 | public final RunTestOnContext rule = new RunTestOnContext();
32 |
33 | private final RedisOptions options = new RedisOptions()
34 | .setType(RedisClientType.CLUSTER)
35 | .setUseReplicas(RedisReplicas.NEVER)
36 | .addConnectionString(redis.getRedisNode0Uri())
37 | .addConnectionString(redis.getRedisNode1Uri())
38 | .addConnectionString(redis.getRedisNode2Uri())
39 | .addConnectionString(redis.getRedisNode3Uri())
40 | .addConnectionString(redis.getRedisNode4Uri())
41 | .addConnectionString(redis.getRedisNode5Uri());
42 |
43 | private Redis client;
44 | private ClusterUtils cluster;
45 |
46 | @Before
47 | public void createClient() {
48 | client = Redis.createClient(rule.vertx(), options);
49 | cluster = new ClusterUtils(rule.vertx(), client);
50 | }
51 |
52 | @After
53 | public void cleanRedis() {
54 | client.close();
55 | }
56 |
57 | @Test
58 | public void test(TestContext test) {
59 | Async async = test.async();
60 |
61 | // slot number: 16287
62 | // keys hashing to the slot: x, exs
63 | int slot = 16287;
64 | String key1 = "x";
65 | String key2 = "exs";
66 |
67 | client.connect().compose(clusterConn -> {
68 | return cluster.connectToMasterThatServesSlot(slot).compose(masterResult -> {
69 | Redis master = masterResult.redis;
70 | RedisConnection masterConn = masterResult.conn;
71 | String masterId = masterResult.id;
72 | return cluster.connectToMasterThatDoesntServeSlot(slot).compose(otherMasterResult -> {
73 | Redis otherMaster = otherMasterResult.redis;
74 | RedisConnection otherMasterConn = otherMasterResult.conn;
75 | String otherMasterId = otherMasterResult.id;
76 | return clusterConn.send(Request.cmd(Command.SET).arg(key1).arg("fubar"))
77 | .compose(ignored -> {
78 | return clusterConn.send(Request.cmd(Command.SET).arg(key2).arg("quux"));
79 | })
80 | .compose(ignored -> {
81 | return otherMasterConn.send(Request.cmd(Command.CLUSTER).arg("SETSLOT").arg(slot).arg("IMPORTING").arg(masterId));
82 | })
83 | .compose(ignored -> {
84 | return masterConn.send(Request.cmd(Command.CLUSTER).arg("SETSLOT").arg(slot).arg("MIGRATING").arg(otherMasterId));
85 | })
86 | .compose(ignored -> {
87 | SocketAddress otherMasterAddr = ((PooledRedisConnection) otherMasterConn).actual().uri().socketAddress();
88 | return masterConn.send(Request.cmd(Command.MIGRATE).arg(otherMasterAddr.host()).arg(otherMasterAddr.port())
89 | .arg("").arg(0).arg(5000).arg("KEYS").arg(key1).arg(key2));
90 | })
91 | .compose(ignored -> {
92 | return masterConn.send(Request.cmd(Command.CLUSTER).arg("SETSLOT").arg(slot).arg("NODE").arg(otherMasterId));
93 | })
94 | .compose(ignored -> {
95 | return otherMasterConn.send(Request.cmd(Command.CLUSTER).arg("SETSLOT").arg(slot).arg("NODE").arg(otherMasterId));
96 | })
97 | .compose(ignored -> {
98 | return clusterConn.send(Request.cmd(Command.GET).arg(key1)); // MOVED
99 | })
100 | .compose(result -> {
101 | test.assertEquals("fubar", result.toString());
102 | return clusterConn.send(Request.cmd(Command.GET).arg(key2)); // not MOVED, slots were reread
103 | })
104 | .compose(result -> {
105 | test.assertEquals("quux", result.toString());
106 | master.close();
107 | otherMaster.close();
108 | return Future.succeededFuture();
109 | });
110 | });
111 | });
112 | }).onComplete(test.asyncAssertSuccess(ignored -> {
113 | async.complete();
114 | }));
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisOptionsTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.core.tracing.TracingPolicy;
4 | import io.vertx.redis.client.RedisClientType;
5 | import io.vertx.redis.client.RedisOptions;
6 | import io.vertx.redis.client.RedisRole;
7 | import org.junit.Test;
8 |
9 | import java.util.ArrayList;
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | import static org.junit.Assert.assertEquals;
14 |
15 | public class RedisOptionsTest {
16 |
17 | @Test
18 | public void testRedisOptions() {
19 | RedisOptions options = new RedisOptions();
20 |
21 | assertEquals(RedisClientType.STANDALONE, options.getType()); // default value
22 | assertEquals(6, options.getMaxPoolSize()); // default value
23 | assertEquals("redis://localhost:6379", options.getEndpoint()); // default value
24 | assertEquals(Collections.singletonList("redis://localhost:6379"), options.getEndpoints()); // default value
25 | }
26 |
27 | @Test
28 | public void testOptionsCopy() {
29 | List endpoints = new ArrayList<>(3);
30 | endpoints.add("redis://localhost:123");
31 | endpoints.add("redis://localhost:124");
32 | endpoints.add("redis://localhost:125");
33 |
34 | // Set values for which there is no default to ensure they are copied correctly
35 | RedisOptions original = new RedisOptions()
36 | .setEndpoints(endpoints)
37 | .setMasterName("someOtherMaster")
38 | .setRole(RedisRole.SENTINEL)
39 | .setPassword("myPassword")
40 | .setTracingPolicy(TracingPolicy.ALWAYS);
41 |
42 | RedisOptions copy = new RedisOptions(original);
43 |
44 | assertEquals(RedisClientType.STANDALONE, copy.getType()); // default value
45 | assertEquals(6, copy.getMaxPoolSize()); // default value
46 | assertEquals(endpoints, copy.getEndpoints());
47 | assertEquals("someOtherMaster", copy.getMasterName());
48 | assertEquals(RedisRole.SENTINEL, copy.getRole());
49 | assertEquals("myPassword", copy.getPassword());
50 | assertEquals(TracingPolicy.ALWAYS, copy.getTracingPolicy());
51 | }
52 |
53 | @Test
54 | public void testFromJsonInstance() {
55 | List endpoints = new ArrayList<>(3);
56 | endpoints.add("redis://localhost:123");
57 | endpoints.add("redis://localhost:124");
58 | endpoints.add("redis://localhost:125");
59 |
60 | RedisOptions original = new RedisOptions()
61 | .setEndpoints(endpoints)
62 | .setMasterName("someOtherMaster")
63 | .setRole(RedisRole.SENTINEL)
64 | .setPassword("myPassword")
65 | .setTracingPolicy(TracingPolicy.ALWAYS);
66 |
67 | RedisOptions copy = new RedisOptions(original.toJson());
68 |
69 | assertEquals(RedisClientType.STANDALONE, copy.getType()); // default value
70 | assertEquals(6, copy.getMaxPoolSize()); // default value
71 | assertEquals(endpoints, copy.getEndpoints());
72 | assertEquals("someOtherMaster", copy.getMasterName());
73 | assertEquals(RedisRole.SENTINEL, copy.getRole());
74 | assertEquals("myPassword", copy.getPassword());
75 | assertEquals(TracingPolicy.ALWAYS, copy.getTracingPolicy());
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisProtocolVersionTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.ext.unit.Async;
4 | import io.vertx.ext.unit.TestContext;
5 | import io.vertx.ext.unit.junit.RunTestOnContext;
6 | import io.vertx.ext.unit.junit.VertxUnitRunner;
7 | import io.vertx.redis.client.Command;
8 | import io.vertx.redis.client.ProtocolVersion;
9 | import io.vertx.redis.client.Redis;
10 | import io.vertx.redis.client.RedisOptions;
11 | import io.vertx.redis.client.Request;
12 | import io.vertx.tests.redis.containers.RedisStandalone;
13 | import org.junit.ClassRule;
14 | import org.junit.Rule;
15 | import org.junit.Test;
16 | import org.junit.runner.RunWith;
17 |
18 | import java.util.Arrays;
19 | import java.util.HashSet;
20 |
21 | @RunWith(VertxUnitRunner.class)
22 | public class RedisProtocolVersionTest {
23 | @ClassRule
24 | public static final RedisStandalone redis = new RedisStandalone();
25 |
26 | @Rule
27 | public final RunTestOnContext rule = new RunTestOnContext();
28 |
29 | @Test
30 | public void resp2(TestContext test) {
31 | RedisOptions options = new RedisOptions()
32 | .setConnectionString(redis.getRedisUri())
33 | .setPreferredProtocolVersion(ProtocolVersion.RESP2);
34 |
35 | Redis client = Redis.createClient(rule.vertx(), options);
36 |
37 | Async async = test.async();
38 | client
39 | .send(Request.cmd(Command.DEL).arg("myhash"))
40 | .flatMap(ignored -> {
41 | return client.send(Request.cmd(Command.HSET).arg("myhash").arg("field1").arg(1).arg("field2").arg(2));
42 | })
43 | .flatMap(response -> {
44 | test.assertEquals(2, response.toInteger());
45 | return client.send(Request.cmd(Command.HGETALL).arg("myhash"));
46 | })
47 | .onSuccess(response -> {
48 | test.assertTrue(response.isArray());
49 |
50 | test.assertTrue(response.containsKey("field1"));
51 | test.assertEquals(1, response.get("field1").toInteger());
52 |
53 | test.assertTrue(response.containsKey("field2"));
54 | test.assertEquals(2, response.get("field2").toInteger());
55 |
56 | test.assertEquals(new HashSet<>(Arrays.asList("field1", "field2")), response.getKeys());
57 |
58 | test.assertEquals("field1", response.get(0).toString());
59 |
60 | client.close();
61 | async.complete();
62 | }).onFailure(test::fail);
63 | }
64 |
65 | @Test
66 | public void resp3(TestContext test) {
67 | RedisOptions options = new RedisOptions()
68 | .setConnectionString(redis.getRedisUri())
69 | .setPreferredProtocolVersion(ProtocolVersion.RESP3);
70 |
71 | Redis client = Redis.createClient(rule.vertx(), options);
72 |
73 | Async async = test.async();
74 | client
75 | .send(Request.cmd(Command.DEL).arg("myhash"))
76 | .flatMap(ignored -> {
77 | return client.send(Request.cmd(Command.HSET).arg("myhash").arg("field1").arg(3).arg("field2").arg(4));
78 | })
79 | .flatMap(response -> {
80 | test.assertEquals(2, response.toInteger());
81 | return client.send(Request.cmd(Command.HGETALL).arg("myhash"));
82 | })
83 | .onSuccess(response -> {
84 | test.assertTrue(response.isMap());
85 |
86 | test.assertTrue(response.containsKey("field1"));
87 | test.assertEquals(3, response.get("field1").toInteger());
88 |
89 | test.assertTrue(response.containsKey("field2"));
90 | test.assertEquals(4, response.get("field2").toInteger());
91 |
92 | test.assertEquals(new HashSet<>(Arrays.asList("field1", "field2")), response.getKeys());
93 |
94 | try {
95 | response.get(0);
96 | test.fail("Map-typed Multi should fail on get(int)");
97 | } catch (Exception expected) {
98 | }
99 |
100 | client.close();
101 | async.complete();
102 | }).onFailure(test::fail);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisPubSubClusterTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.ext.unit.Async;
4 | import io.vertx.ext.unit.TestContext;
5 | import io.vertx.ext.unit.junit.RunTestOnContext;
6 | import io.vertx.ext.unit.junit.VertxUnitRunner;
7 | import io.vertx.redis.client.Command;
8 | import io.vertx.redis.client.EventBusHandler;
9 | import io.vertx.redis.client.Redis;
10 | import io.vertx.redis.client.RedisConnection;
11 | import io.vertx.redis.client.RedisOptions;
12 | import io.vertx.redis.client.Request;
13 | import io.vertx.tests.redis.containers.RedisCluster;
14 | import org.junit.After;
15 | import org.junit.Before;
16 | import org.junit.ClassRule;
17 | import org.junit.Rule;
18 | import org.junit.Test;
19 | import org.junit.runner.RunWith;
20 |
21 | @RunWith(VertxUnitRunner.class)
22 | public class RedisPubSubClusterTest {
23 |
24 | @ClassRule
25 | public static final RedisCluster redis = new RedisCluster();
26 |
27 | @Rule
28 | public final RunTestOnContext rule = new RunTestOnContext();
29 |
30 | private Redis redisPublish;
31 | private Redis redisSubscribe;
32 |
33 | private RedisConnection pubConn;
34 | private RedisConnection subConn;
35 |
36 | @Before
37 | public void before(TestContext should) {
38 | Async test = should.async();
39 | RedisOptions options = new RedisOptions().setConnectionString(redis.getRedisNode0Uri());
40 |
41 | redisPublish = Redis.createClient(rule.vertx(), options);
42 | redisPublish.connect().onComplete(should.asyncAssertSuccess(connectPub -> {
43 | pubConn = connectPub;
44 |
45 | redisSubscribe = Redis.createClient(rule.vertx(), options);
46 | redisSubscribe.connect().onComplete(should.asyncAssertSuccess(connectSub -> {
47 | subConn = connectSub;
48 |
49 | test.complete();
50 | }));
51 | }));
52 | }
53 |
54 | @After
55 | public void after() {
56 | redisPublish.close();
57 | redisSubscribe.close();
58 | }
59 |
60 | @Test
61 | public void testSubscribeMultipleTimes(TestContext should) {
62 | final int N = 10;
63 | final String channel = "chan1";
64 |
65 | final Async test = should.async(N);
66 | for (int i = 0; i < N; i++) {
67 | rule.vertx().eventBus().consumer(channel, msg -> {
68 | test.countDown();
69 | });
70 | }
71 |
72 | rule.vertx().eventBus().consumer("io.vertx.redis." + channel, msg -> {
73 | rule.vertx().eventBus().publish(channel, msg.body());
74 | });
75 |
76 | subConn.handler(EventBusHandler.create(rule.vertx()));
77 | subUnsub(channel, N, should.async(N));
78 |
79 | rule.vertx().setTimer(1000, id -> {
80 | pubConn.send(Request.cmd(Command.PUBLISH).arg(channel).arg("hello"))
81 | .onComplete(should.asyncAssertSuccess());
82 | });
83 | }
84 |
85 | private void subUnsub(String channel, int attempts, Async testSub) {
86 | subConn.send(Request.cmd(Command.UNSUBSCRIBE).arg(channel)).onComplete(unreply -> {
87 | subConn.send(Request.cmd(Command.SUBSCRIBE).arg(channel)).onComplete(reply -> {
88 | testSub.countDown();
89 | if (attempts > 1) {
90 | subUnsub(channel, attempts - 1, testSub);
91 | }
92 | });
93 | });
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisReconnectTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.core.Context;
4 | import io.vertx.ext.unit.Async;
5 | import io.vertx.ext.unit.TestContext;
6 | import io.vertx.ext.unit.junit.RunTestOnContext;
7 | import io.vertx.ext.unit.junit.VertxUnitRunner;
8 | import io.vertx.redis.client.Command;
9 | import io.vertx.redis.client.Redis;
10 | import io.vertx.redis.client.RedisConnection;
11 | import io.vertx.redis.client.RedisOptions;
12 | import io.vertx.redis.client.Request;
13 | import io.vertx.tests.redis.containers.RedisStandalone;
14 | import org.junit.After;
15 | import org.junit.Before;
16 | import org.junit.ClassRule;
17 | import org.junit.Rule;
18 | import org.junit.Test;
19 | import org.junit.runner.RunWith;
20 |
21 | @RunWith(VertxUnitRunner.class)
22 | public class RedisReconnectTest {
23 |
24 | private static final int RETRIES = 10;
25 |
26 | @ClassRule
27 | public static final RedisStandalone redis = new RedisStandalone();
28 |
29 | @Rule
30 | public final RunTestOnContext rule = new RunTestOnContext();
31 |
32 | private Redis client;
33 |
34 | // this connection will mutate during tests with re-connect
35 | private RedisConnection connection;
36 |
37 | @Before
38 | public void before(TestContext should) {
39 | final Async before = should.async();
40 |
41 | Context context = rule.vertx().getOrCreateContext();
42 | client = Redis.createClient(rule.vertx(), new RedisOptions().setConnectionString(redis.getRedisUri()));
43 | client.connect().onComplete(onConnect -> {
44 | should.assertTrue(onConnect.succeeded());
45 | should.assertEquals(context, rule.vertx().getOrCreateContext());
46 | connection = onConnect.result();
47 | before.complete();
48 | });
49 | }
50 |
51 | @After
52 | public void after() {
53 | client.close();
54 | }
55 |
56 | @Test
57 | public void testConnection(TestContext should) {
58 | final Async test = should.async();
59 |
60 | connection
61 | .exceptionHandler(should::fail)
62 | .endHandler(end -> {
63 | // the connection was closed, will reconnect
64 | reconnect(0);
65 | })
66 | .send(Request.cmd(Command.CLIENT).arg("LIST"))
67 | .onFailure(should::fail)
68 | .onSuccess(list -> {
69 | String res = list.toString();
70 | // this is a hack
71 | String id = res.substring(3, res.indexOf(' '));
72 |
73 | // kill the connection
74 | final RedisConnection orig = connection;
75 |
76 | connection
77 | .send(Request.cmd(Command.CLIENT).arg("KILL").arg("SKIPME").arg("no").arg("ID").arg(id))
78 | .onFailure(should::fail)
79 | .onSuccess(kill -> {
80 | should.assertEquals(1, kill.toInteger());
81 | // wait until the connection is updated
82 | rule.vertx()
83 | .setPeriodic(500, t -> {
84 | if (orig != connection) {
85 | // when the 2 references change
86 | // it means the connection has been replaced
87 | test.complete();
88 | }
89 | });
90 | });
91 |
92 | });
93 | }
94 |
95 | private void reconnect(int retry) {
96 | if (retry < RETRIES) {
97 | // retry with backoff
98 | long backoff = (long) (Math.pow(2, Math.min(retry, RETRIES)) * RETRIES);
99 |
100 | rule
101 | .vertx()
102 | .setTimer(backoff, timer -> client.connect().onComplete(onReconnect -> {
103 | if (onReconnect.failed()) {
104 | reconnect(retry + 1);
105 | } else {
106 | connection = onReconnect.result();
107 | }
108 | }));
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisSentinelMasterFailoverTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.ext.unit.Async;
4 | import io.vertx.ext.unit.TestContext;
5 | import io.vertx.ext.unit.junit.RunTestOnContext;
6 | import io.vertx.ext.unit.junit.VertxUnitRunner;
7 | import io.vertx.redis.client.Command;
8 | import io.vertx.redis.client.Redis;
9 | import io.vertx.redis.client.RedisClientType;
10 | import io.vertx.redis.client.RedisOptions;
11 | import io.vertx.redis.client.RedisRole;
12 | import io.vertx.redis.client.Request;
13 | import io.vertx.tests.redis.containers.RedisSentinel;
14 | import org.junit.ClassRule;
15 | import org.junit.Rule;
16 | import org.junit.Test;
17 | import org.junit.runner.RunWith;
18 |
19 | import static io.vertx.tests.redis.client.TestUtils.retryUntilSuccess;
20 |
21 | @RunWith(VertxUnitRunner.class)
22 | public class RedisSentinelMasterFailoverTest {
23 | @ClassRule
24 | public static final RedisSentinel redis = new RedisSentinel();
25 |
26 | @Rule
27 | public final RunTestOnContext rule = new RunTestOnContext();
28 |
29 | @Test
30 | public void test(TestContext test) {
31 | Async async = test.async();
32 |
33 | Redis.createClient(
34 | rule.vertx(),
35 | new RedisOptions()
36 | .setType(RedisClientType.SENTINEL)
37 | .addConnectionString(redis.getRedisSentinel0Uri())
38 | .addConnectionString(redis.getRedisSentinel1Uri())
39 | .addConnectionString(redis.getRedisSentinel2Uri())
40 | .setRole(RedisRole.MASTER)
41 | .setAutoFailover(true))
42 | .connect()
43 | .onComplete(test.asyncAssertSuccess(conn -> {
44 | conn.send(Request.cmd(Command.SET).arg("key").arg("value"))
45 | .compose(ignored -> conn.send(Request.cmd(Command.SHUTDOWN)))
46 | .onComplete(test.asyncAssertFailure(ignored -> { // connection closed
47 | retryUntilSuccess(rule.vertx(), () -> conn.send(Request.cmd(Command.GET).arg("key")), 50)
48 | .onComplete(test.asyncAssertSuccess(response -> {
49 | test.assertEquals("value", response.toString());
50 | async.complete();
51 | }));
52 | }));
53 | }));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisThreadConsistencyTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.core.Future;
4 | import io.vertx.core.VerticleBase;
5 | import io.vertx.core.http.HttpMethod;
6 | import io.vertx.ext.unit.Async;
7 | import io.vertx.ext.unit.TestContext;
8 | import io.vertx.ext.unit.junit.RunTestOnContext;
9 | import io.vertx.ext.unit.junit.VertxUnitRunner;
10 | import io.vertx.redis.client.Redis;
11 | import io.vertx.redis.client.RedisAPI;
12 | import io.vertx.redis.client.RedisOptions;
13 | import io.vertx.tests.redis.containers.RedisStandalone;
14 | import org.junit.ClassRule;
15 | import org.junit.Rule;
16 | import org.junit.Test;
17 | import org.junit.runner.RunWith;
18 |
19 | import java.util.Collections;
20 |
21 | @RunWith(VertxUnitRunner.class)
22 | public class RedisThreadConsistencyTest {
23 |
24 | @ClassRule
25 | public static final RedisStandalone redis = new RedisStandalone();
26 |
27 | @Rule
28 | public final RunTestOnContext rule = new RunTestOnContext();
29 |
30 | @Test
31 | public void redisThreadConsistencyTest(TestContext should) {
32 | final Async test = should.async();
33 |
34 | rule.vertx()
35 | .deployVerticle(new Verticle(
36 | RedisAPI.api(
37 | Redis.createClient(
38 | rule.vertx(),
39 | new RedisOptions().setConnectionString(redis.getRedisUri())))))
40 | .onFailure(should::fail)
41 | .onSuccess(id -> {
42 | rule.vertx()
43 | .createHttpClient()
44 | .request(HttpMethod.GET, 8080, "localhost", "/")
45 | .onFailure(should::fail)
46 | .onSuccess(req -> {
47 | req.send()
48 | .onFailure(should::fail)
49 | .onSuccess(res -> {
50 | should.assertEquals(res.getHeader("initialThread"), res.getHeader("threadAfterRedisExecution"));
51 | test.complete();
52 | });
53 | });
54 | });
55 | }
56 |
57 | static class Verticle extends VerticleBase {
58 |
59 | RedisAPI redisAPI;
60 |
61 | Verticle(RedisAPI redisApi) {
62 | this.redisAPI = redisApi;
63 | }
64 |
65 | @Override
66 | public Future> start() {
67 | return vertx.createHttpServer()
68 | .requestHandler(req -> {
69 | String threadBeforeRedisCommandExecution = Thread.currentThread().getName();
70 | redisAPI.info(Collections.emptyList()).onComplete(result -> {
71 | String threadAfterRedisCommandExecution = Thread.currentThread().getName();
72 | req.response().putHeader("initialThread", threadBeforeRedisCommandExecution)
73 | .putHeader("threadAfterRedisExecution", threadAfterRedisCommandExecution)
74 | .end("Ok");
75 | });
76 | })
77 | .listen(8080);
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/RedisTracingTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.core.Context;
4 | import io.vertx.core.Vertx;
5 | import io.vertx.core.spi.tracing.SpanKind;
6 | import io.vertx.core.spi.tracing.TagExtractor;
7 | import io.vertx.core.spi.tracing.VertxTracer;
8 | import io.vertx.core.tracing.TracingPolicy;
9 | import io.vertx.ext.unit.Async;
10 | import io.vertx.ext.unit.TestContext;
11 | import io.vertx.ext.unit.junit.VertxUnitRunner;
12 | import io.vertx.redis.client.Command;
13 | import io.vertx.redis.client.Redis;
14 | import io.vertx.redis.client.RedisOptions;
15 | import io.vertx.redis.client.Request;
16 | import io.vertx.redis.client.impl.CommandImpl;
17 | import io.vertx.tests.redis.containers.RedisStandalone;
18 | import org.junit.After;
19 | import org.junit.Before;
20 | import org.junit.ClassRule;
21 | import org.junit.Test;
22 | import org.junit.runner.RunWith;
23 |
24 | import java.util.*;
25 | import java.util.function.BiConsumer;
26 |
27 | @RunWith(VertxUnitRunner.class)
28 | public class RedisTracingTest {
29 | @ClassRule
30 | public static final RedisStandalone redis = new RedisStandalone();
31 |
32 | Vertx vertx;
33 | VertxTracer tracer;
34 | Redis client;
35 |
36 | @Before
37 | public void setup() {
38 | vertx = Vertx.builder()
39 | .withTracer(ignored -> new VertxTracer() {
40 | @Override
41 | public Object sendRequest(Context context, SpanKind kind, TracingPolicy tracingPolicy, Object request, String operation, BiConsumer headers, TagExtractor tagExtractor) {
42 | return tracer.sendRequest(context, kind, tracingPolicy, request, operation, headers, tagExtractor);
43 | }
44 | @Override
45 | public void receiveResponse(Context context, Object response, Object payload, Throwable failure, TagExtractor tagExtractor) {
46 | tracer.receiveResponse(context, response, payload, failure, tagExtractor);
47 | }
48 | }).build();
49 | client = Redis.createClient(vertx, new RedisOptions().setConnectionString(redis.getRedisUri()));
50 | }
51 |
52 | @After
53 | public void teardown(TestContext test) {
54 | vertx.close().onComplete(test.asyncAssertSuccess());
55 | }
56 |
57 | @Test
58 | public void success(TestContext test) {
59 | testTracing(test, Request.cmd(Command.PING), true);
60 | }
61 |
62 | @Test
63 | public void failure(TestContext test) {
64 | testTracing(test, Request.cmd(new CommandImpl("NONEXISTING COMMAND", 0, true, false, false)), false);
65 | }
66 |
67 | private void testTracing(TestContext test, Request clientRequest, boolean success) {
68 | Async async = test.async();
69 |
70 | Object trace = new Object();
71 | List actions = Collections.synchronizedList(new ArrayList<>());
72 |
73 | tracer = new VertxTracer() {
74 | @Override
75 | public Object sendRequest(Context context, SpanKind kind, TracingPolicy policy, Object request, String operation, BiConsumer headers, TagExtractor tagExtractor) {
76 | Map tags = tagExtractor.extract(request);
77 | String redisPort = String.valueOf(redis.getPort());
78 | test.assertEquals("client", tags.get("span.kind"));
79 | test.assertEquals("redis", tags.get("db.system"));
80 | test.assertEquals("127.0.0.1", tags.get("network.peer.address"));
81 | test.assertEquals(redisPort, tags.get("network.peer.port"));
82 | test.assertEquals("localhost", tags.get("server.address"));
83 | test.assertEquals(redisPort, tags.get("server.port"));
84 | test.assertEquals(clientRequest.command().toString(), tags.get("db.operation.name"));
85 | actions.add("sendRequest");
86 | return trace;
87 | }
88 |
89 | @Override
90 | public void receiveResponse(Context context, Object response, Object payload, Throwable failure, TagExtractor tagExtractor) {
91 | test.assertTrue(payload == trace);
92 | if (success) {
93 | test.assertNotNull(response);
94 | test.assertNull(failure);
95 | } else {
96 | test.assertNull(response);
97 | test.assertNotNull(failure);
98 | }
99 | actions.add("receiveResponse");
100 | }
101 | };
102 |
103 | vertx.runOnContext(ignored -> {
104 | client.send(clientRequest).onComplete(result -> {
105 | test.assertEquals(success, result.succeeded());
106 | test.assertEquals(Arrays.asList("sendRequest", "receiveResponse"), actions);
107 | async.complete();
108 | });
109 | });
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/SharedRedisConnectionTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.core.*;
4 | import io.vertx.ext.unit.Async;
5 | import io.vertx.ext.unit.TestContext;
6 | import io.vertx.ext.unit.junit.VertxUnitRunner;
7 | import io.vertx.redis.client.Redis;
8 | import io.vertx.redis.client.RedisAPI;
9 | import io.vertx.redis.client.RedisOptions;
10 | import io.vertx.redis.client.Response;
11 | import io.vertx.tests.redis.containers.RedisStandalone;
12 | import org.junit.After;
13 | import org.junit.Before;
14 | import org.junit.ClassRule;
15 | import org.junit.Test;
16 | import org.junit.runner.RunWith;
17 |
18 | import java.util.Arrays;
19 |
20 | @RunWith(VertxUnitRunner.class)
21 | public class SharedRedisConnectionTest {
22 |
23 | @ClassRule
24 | public static final RedisStandalone redis = new RedisStandalone();
25 |
26 | private static final int VERTICLES_COUNT = 10;
27 | private static final int ITERATIONS_COUNT = 1000;
28 |
29 | private static final String REDIS_NUMBER_VALUE_KEY = "user:post:pinned:1372";
30 | private static final String REDIS_SET_VALUE_KEY = "user:like:post:975";
31 |
32 | Vertx vertx;
33 | RedisAPI conn;
34 |
35 | @Before
36 | public void setup(TestContext test) {
37 | Async async = test.async();
38 | vertx = Vertx.vertx();
39 | RedisOptions options = new RedisOptions()
40 | .setConnectionString(redis.getRedisUri())
41 | .setMaxWaitingHandlers(VERTICLES_COUNT * ITERATIONS_COUNT * 2); // 2 requests per iteration
42 | Redis.createClient(vertx, options)
43 | .connect()
44 | .map(RedisAPI::api)
45 | .flatMap(api -> {
46 | return api.set(Arrays.asList(REDIS_NUMBER_VALUE_KEY, "42"))
47 | .map(api);
48 | }).flatMap(api -> {
49 | return api.sadd(Arrays.asList(REDIS_SET_VALUE_KEY, "100", "101", "102"))
50 | .map(api);
51 | })
52 | .onComplete(result -> {
53 | if (result.succeeded()) {
54 | conn = result.result();
55 | } else {
56 | test.fail(result.cause());
57 | }
58 | async.complete();
59 | });
60 | }
61 |
62 | @After
63 | public void teardown(TestContext test) {
64 | conn.close();
65 | vertx.close().onComplete(test.asyncAssertSuccess());
66 | }
67 |
68 | @Test
69 | public void test(TestContext test) {
70 | vertx.deployVerticle(() -> new MyVerticle(conn, test), new DeploymentOptions().setInstances(VERTICLES_COUNT));
71 | }
72 |
73 | public static class MyVerticle extends VerticleBase {
74 | private final RedisAPI conn;
75 | private final TestContext test;
76 |
77 | public MyVerticle(RedisAPI conn, TestContext test) {
78 | this.conn = conn;
79 | this.test = test;
80 | }
81 |
82 | @Override
83 | public Future> start() throws Exception {
84 | Async async = test.async(ITERATIONS_COUNT);
85 | for (int i = 0; i < ITERATIONS_COUNT; i++) {
86 | test()
87 | .onSuccess(ignored -> async.countDown())
88 | .onFailure(test::fail);
89 | }
90 | return super.start();
91 | }
92 |
93 | private Future> test() {
94 | Future fetchNumberFuture = conn.get(REDIS_NUMBER_VALUE_KEY)
95 | .onSuccess(response -> {
96 | try {
97 | response.toInteger();
98 | } catch (Exception e) {
99 | test.fail(e);
100 | }
101 | });
102 |
103 | Future fetchSetFuture = conn.smembers(REDIS_SET_VALUE_KEY)
104 | .onSuccess(response -> {
105 | try {
106 | for (Response part : response) {
107 | part.toInteger();
108 | }
109 | } catch (Exception e) {
110 | test.fail(e);
111 | }
112 | });
113 |
114 | return Future.all(fetchNumberFuture, fetchSetFuture);
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/client/TestUtils.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.client;
2 |
3 | import io.vertx.core.Completable;
4 | import io.vertx.core.Future;
5 | import io.vertx.core.Promise;
6 | import io.vertx.core.Vertx;
7 |
8 | import java.util.UUID;
9 | import java.util.function.Supplier;
10 |
11 | public class TestUtils {
12 | public static String randomKey() {
13 | return UUID.randomUUID().toString();
14 | }
15 |
16 | public static Future retryUntilSuccess(Vertx vertx, Supplier> action, int maxRetries) {
17 | Promise promise = Promise.promise();
18 | retryUntilSuccess(vertx, action, maxRetries, promise);
19 | return promise.future();
20 | }
21 |
22 | private static void retryUntilSuccess(Vertx vertx, Supplier> action, int maxRetries, Completable promise) {
23 | action.get().onComplete(result -> {
24 | if (result.succeeded()) {
25 | promise.succeed(result.result());
26 | } else {
27 | if (maxRetries < 1) {
28 | promise.fail(result.cause());
29 | } else {
30 | vertx.setTimer(500, ignored -> {
31 | retryUntilSuccess(vertx, action, maxRetries - 1, promise);
32 | });
33 | }
34 | }
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/containers/RedisCluster.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.containers;
2 |
3 | import org.junit.rules.TestRule;
4 | import org.junit.runner.Description;
5 | import org.junit.runners.model.Statement;
6 | import org.testcontainers.containers.FixedHostPortGenericContainer;
7 | import org.testcontainers.containers.GenericContainer;
8 | import org.testcontainers.containers.wait.strategy.Wait;
9 | import org.testcontainers.containers.wait.strategy.WaitAllStrategy;
10 |
11 | import java.io.IOException;
12 |
13 | public class RedisCluster implements TestRule {
14 | private final GenericContainer> container = new FixedHostPortGenericContainer<>("quay.io/ladicek/redis-cluster")
15 | .withFixedExposedPort(7000, 7000)
16 | .withFixedExposedPort(7001, 7001)
17 | .withFixedExposedPort(7002, 7002)
18 | .withFixedExposedPort(7003, 7003)
19 | .withFixedExposedPort(7004, 7004)
20 | .withFixedExposedPort(7005, 7005)
21 | .withFixedExposedPort(7006, 7006) // possible extra node, not present by default
22 | .withFixedExposedPort(7007, 7007) // possible extra node, not present by default
23 | .waitingFor(new WaitAllStrategy()
24 | .withStrategy(Wait.forLogMessage(".*Cluster state changed: ok.*", 6))
25 | .withStrategy(Wait.forSuccessfulCommand("/cluster-slots-expected-lines.sh 7000 7005 30")));
26 |
27 | @Override
28 | public Statement apply(Statement base, Description description) {
29 | return container.apply(base, description);
30 | }
31 |
32 | public String getRedisNode0Uri() {
33 | return getRedisUri(7000);
34 | }
35 |
36 | public String getRedisNode1Uri() {
37 | return getRedisUri(7001);
38 | }
39 |
40 | public String getRedisNode2Uri() {
41 | return getRedisUri(7002);
42 | }
43 |
44 | public String getRedisNode3Uri() {
45 | return getRedisUri(7003);
46 | }
47 |
48 | public String getRedisNode4Uri() {
49 | return getRedisUri(7004);
50 | }
51 |
52 | public String getRedisNode5Uri() {
53 | return getRedisUri(7005);
54 | }
55 |
56 | private String getRedisUri(int portNumber) {
57 | return "redis://" + container.getHost() + ":" + container.getMappedPort(portNumber);
58 | }
59 |
60 | public void addMaster(int portNumber) {
61 | execute("/cluster-add-master.sh", "" + portNumber);
62 | }
63 |
64 | private void execute(String... command) {
65 | try {
66 | container.execInContainer(command);
67 | } catch (IOException | InterruptedException e) {
68 | throw new RuntimeException(e);
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/containers/RedisReplication.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.containers;
2 |
3 | import org.junit.rules.TestRule;
4 | import org.junit.runner.Description;
5 | import org.junit.runners.model.Statement;
6 | import org.testcontainers.containers.FixedHostPortGenericContainer;
7 | import org.testcontainers.containers.GenericContainer;
8 | import org.testcontainers.containers.wait.strategy.Wait;
9 |
10 | public class RedisReplication implements TestRule {
11 | private final GenericContainer> container = new FixedHostPortGenericContainer<>("quay.io/ladicek/redis-replication")
12 | .withFixedExposedPort(7000, 7000)
13 | .withFixedExposedPort(7001, 7001)
14 | .withFixedExposedPort(7002, 7002)
15 | .waitingFor(Wait.forLogMessage(".*MASTER <-> REPLICA sync: Finished with success.*", 2));
16 |
17 | @Override
18 | public Statement apply(Statement base, Description description) {
19 | return container.apply(base, description);
20 | }
21 |
22 | public String getRedisMasterUri() {
23 | return getRedisUri(7000);
24 | }
25 |
26 | public String getRedisReplica0Uri() {
27 | return getRedisUri(7001);
28 | }
29 |
30 | public String getRedisReplica1Uri() {
31 | return getRedisUri(7002);
32 | }
33 |
34 | private String getRedisUri(int portNumber) {
35 | return "redis://" + container.getHost() + ":" + container.getMappedPort(portNumber);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/containers/RedisSentinel.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.containers;
2 |
3 | import org.junit.rules.TestRule;
4 | import org.junit.runner.Description;
5 | import org.junit.runners.model.Statement;
6 | import org.testcontainers.containers.FixedHostPortGenericContainer;
7 | import org.testcontainers.containers.GenericContainer;
8 | import org.testcontainers.containers.wait.strategy.Wait;
9 |
10 | public class RedisSentinel implements TestRule {
11 | private final GenericContainer> container = new FixedHostPortGenericContainer<>("quay.io/ladicek/redis-replication")
12 | .withEnv("SENTINEL", "true")
13 | .withFixedExposedPort(7000, 7000)
14 | .withFixedExposedPort(7001, 7001)
15 | .withFixedExposedPort(7002, 7002)
16 | .withFixedExposedPort(5000, 5000)
17 | .withFixedExposedPort(5001, 5001)
18 | .withFixedExposedPort(5002, 5002)
19 | .waitingFor(Wait.forLogMessage(".*\\+slave.*", 6));
20 |
21 | @Override
22 | public Statement apply(Statement base, Description description) {
23 | return container.apply(base, description);
24 | }
25 |
26 | public String getRedisMasterUri() {
27 | return getRedisUri(7000);
28 | }
29 |
30 | public String getRedisReplica0Uri() {
31 | return getRedisUri(7001);
32 | }
33 |
34 | public String getRedisReplica1Uri() {
35 | return getRedisUri(7002);
36 | }
37 |
38 | public String getRedisSentinel0Uri() {
39 | return getRedisUri(5000);
40 | }
41 |
42 | public String getRedisSentinel1Uri() {
43 | return getRedisUri(5001);
44 | }
45 |
46 | public String getRedisSentinel2Uri() {
47 | return getRedisUri(5002);
48 | }
49 |
50 | private String getRedisUri(int portNumber) {
51 | return "redis://" + container.getHost() + ":" + container.getMappedPort(portNumber);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/containers/RedisStandalone.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.containers;
2 |
3 | import org.junit.rules.TestRule;
4 | import org.junit.runner.Description;
5 | import org.junit.runners.model.Statement;
6 | import org.testcontainers.containers.GenericContainer;
7 | import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
8 | import org.testcontainers.images.builder.Transferable;
9 |
10 | import java.util.HashMap;
11 | import java.util.Map;
12 | import java.util.Objects;
13 |
14 | public class RedisStandalone implements TestRule {
15 | public static class Builder {
16 | private String version;
17 | private String password;
18 |
19 | private boolean tlsEnabled;
20 | private KeyPairAndCertificate serverKey;
21 |
22 | private boolean mutualTlsEnabled;
23 | private KeyPairAndCertificate clientKey;
24 |
25 | private Builder() {
26 | }
27 |
28 | public Builder setVersion(String version) {
29 | this.version = Objects.requireNonNull(version);
30 | return this;
31 | }
32 |
33 | public Builder setPassword(String password) {
34 | this.password = Objects.requireNonNull(password);
35 | return this;
36 | }
37 |
38 | public Builder enableTls(KeyPairAndCertificate serverKey) {
39 | this.tlsEnabled = true;
40 | this.serverKey = Objects.requireNonNull(serverKey);
41 | return this;
42 | }
43 |
44 | public Builder enableMutualTls(KeyPairAndCertificate clientKey) {
45 | assert tlsEnabled;
46 | this.mutualTlsEnabled = true;
47 | this.clientKey = Objects.requireNonNull(clientKey);
48 | return this;
49 | }
50 |
51 | public RedisStandalone build() {
52 | return new RedisStandalone(this);
53 | }
54 | }
55 |
56 | public static Builder builder() {
57 | return new Builder();
58 | }
59 |
60 | // ---
61 |
62 | private final GenericContainer> container;
63 |
64 | public RedisStandalone() {
65 | this(builder());
66 | }
67 |
68 | private RedisStandalone(Builder builder) {
69 | String image = "docker.io/bitnami/redis:" + (builder.version != null ? builder.version : "7.2");
70 |
71 | Map env = new HashMap<>();
72 | if (builder.password != null) {
73 | env.put("REDIS_PASSWORD", builder.password);
74 | } else {
75 | env.put("ALLOW_EMPTY_PASSWORD", "yes");
76 | }
77 |
78 | if (builder.tlsEnabled) {
79 | env.put("REDIS_TLS_ENABLED", "yes");
80 | env.put("REDIS_TLS_KEY_FILE", "/certs/redis.key");
81 | env.put("REDIS_TLS_CERT_FILE", "/certs/redis.crt");
82 |
83 | if (builder.mutualTlsEnabled) {
84 | env.put("REDIS_TLS_CA_FILE", "/certs/client.crt");
85 | } else {
86 | env.put("REDIS_TLS_AUTH_CLIENTS", "no");
87 | }
88 | }
89 |
90 | this.container = new GenericContainer<>(image)
91 | .withEnv(env)
92 | .withExposedPorts(6379)
93 | .waitingFor(new LogMessageWaitStrategy().withRegEx(".*Ready to accept connections.*"));
94 |
95 | if (builder.tlsEnabled) {
96 | container.withCopyToContainer(Transferable.of(builder.serverKey.privateKeyAsPEM()), "/certs/redis.key");
97 | container.withCopyToContainer(Transferable.of(builder.serverKey.certificateAsPEM()), "/certs/redis.crt");
98 | }
99 | if (builder.mutualTlsEnabled) {
100 | container.withCopyToContainer(Transferable.of(builder.clientKey.certificateAsPEM()), "/certs/client.crt");
101 | }
102 | }
103 |
104 | @Override
105 | public Statement apply(Statement base, Description description) {
106 | return container.apply(base, description);
107 | }
108 |
109 | public String getHost() {
110 | return container.getHost();
111 | }
112 |
113 | public int getPort() {
114 | return container.getMappedPort(6379);
115 | }
116 |
117 | public String getRedisUri() {
118 | return "redis://" + getHost() + ":" + getPort();
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/internal/ArrayQueueTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.internal;
2 |
3 | import io.vertx.redis.client.impl.ArrayQueue;
4 | import org.junit.Ignore;
5 | import org.junit.Test;
6 |
7 | import static org.junit.Assert.assertEquals;
8 | import static org.junit.Assert.assertNotNull;
9 |
10 | public class ArrayQueueTest {
11 |
12 | @Test
13 | @Ignore("This test is very CPU intensive and causes trouble on CI")
14 | public void testOverflow() {
15 | ArrayQueue arrayQueue = new ArrayQueue(10);
16 |
17 | arrayQueue.offer(0);
18 | for (int i = 0; i < Integer.MAX_VALUE; i++) {
19 | arrayQueue.offer(0);
20 | arrayQueue.poll();
21 | arrayQueue.offer(0);
22 | arrayQueue.poll();
23 | }
24 | assertEquals(9, arrayQueue.freeSlots());
25 | assertEquals(0, (int) arrayQueue.poll());
26 | assertEquals(10, arrayQueue.freeSlots());
27 | //Overflow for int back
28 | arrayQueue.offer(1);
29 | //Overflow for int front
30 | assertEquals(1, (int) arrayQueue.poll());
31 | }
32 |
33 | @Test
34 | public void testPollNSE() {
35 | ArrayQueue arrayQueue = new ArrayQueue(10);
36 | arrayQueue.poll();
37 | arrayQueue.offer("a");
38 | assertNotNull(arrayQueue.poll());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/internal/BufferTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.internal;
2 |
3 | import io.netty.buffer.Unpooled;
4 | import io.vertx.core.buffer.Buffer;
5 | import io.vertx.core.internal.buffer.BufferInternal;
6 | import io.vertx.redis.client.Command;
7 | import io.vertx.redis.client.Request;
8 | import io.vertx.redis.client.impl.RequestImpl;
9 | import org.junit.Test;
10 |
11 | import java.nio.charset.Charset;
12 | import java.nio.charset.StandardCharsets;
13 |
14 | public class BufferTest {
15 |
16 | private static final int iterations = 50000;
17 |
18 | @Test
19 | public void testAppendString() {
20 | Request hmset = Request.cmd(Command.HMSET);
21 | String key = "mykey";
22 |
23 | for (int i = 0; i < iterations; i++) {
24 | hmset.arg(key).arg(i);
25 | }
26 |
27 | System.out.println(((RequestImpl) hmset).encode().length());
28 |
29 | for (int j = 0; j < 5; j++) {
30 | final long t0 = System.nanoTime();
31 | hmset = Request.cmd(Command.HMSET);
32 |
33 | for (int i = 0; i < iterations; i++) {
34 | hmset.arg(key).arg(i);
35 | }
36 | final long t1 = System.nanoTime();
37 |
38 | System.out.println(((RequestImpl) hmset).encode().length() + "| t " + (t1 - t0));
39 | }
40 | System.out.println("---");
41 | }
42 |
43 | @Test
44 | public void testAppendBytes() {
45 | Request hmset = Request.cmd(Command.HMSET);
46 | byte[] key = "my-key".getBytes(StandardCharsets.US_ASCII);
47 |
48 | for (int i = 0; i < iterations; i++) {
49 | hmset.arg(key).arg(i);
50 | }
51 |
52 | System.out.println(((RequestImpl) hmset).encode().length());
53 |
54 | for (int j = 0; j < 5; j++) {
55 | final long t0 = System.nanoTime();
56 | hmset = Request.cmd(Command.HMSET);
57 |
58 | for (int i = 0; i < iterations; i++) {
59 | hmset.arg(key).arg(i);
60 | }
61 | final long t1 = System.nanoTime();
62 |
63 | System.out.println(((RequestImpl) hmset).encode().length() + "| t " + (t1 - t0));
64 | }
65 | System.out.println("---");
66 | }
67 |
68 | @Test
69 | public void testAppendBuffer() {
70 | Request hmset = Request.cmd(Command.HMSET);
71 | Buffer key = Buffer.buffer("my-key");
72 |
73 | for (int i = 0; i < iterations; i++) {
74 | hmset.arg(key).arg(i);
75 | }
76 |
77 | System.out.println(((RequestImpl) hmset).encode().length());
78 |
79 | for (int j = 0; j < 5; j++) {
80 | final long t0 = System.nanoTime();
81 | hmset = Request.cmd(Command.HMSET);
82 |
83 | for (int i = 0; i < iterations; i++) {
84 | hmset.arg(key).arg(i);
85 | }
86 | final long t1 = System.nanoTime();
87 |
88 | System.out.println(((RequestImpl) hmset).encode().length() + "| t " + (t1 - t0));
89 | }
90 | System.out.println("---");
91 | }
92 |
93 | @Test
94 | public void testAppendStringToBytes() {
95 | Request hmset = Request.cmd(Command.HMSET);
96 | String key = "mykey";
97 |
98 | for (int i = 0; i < iterations; i++) {
99 | hmset.arg(key.getBytes()).arg(i);
100 | }
101 |
102 | System.out.println(((RequestImpl) hmset).encode().length());
103 |
104 | for (int j = 0; j < 5; j++) {
105 | final long t0 = System.nanoTime();
106 | hmset = Request.cmd(Command.HMSET);
107 |
108 | for (int i = 0; i < iterations; i++) {
109 | hmset.arg(key.getBytes()).arg(i);
110 | }
111 | final long t1 = System.nanoTime();
112 |
113 | System.out.println(((RequestImpl) hmset).encode().length() + "| t " + (t1 - t0));
114 | }
115 | System.out.println("---");
116 | }
117 |
118 | @Test
119 | public void testAppendBufferWrapped() {
120 | Charset UTF8 = StandardCharsets.UTF_8;
121 | Request hmset = Request.cmd(Command.HMSET);
122 | Buffer key = BufferInternal.buffer(Unpooled.wrappedBuffer(UTF8.encode("my-key")));
123 |
124 | for (int i = 0; i < iterations; i++) {
125 | hmset.arg(key).arg(i);
126 | }
127 |
128 | System.out.println(((RequestImpl) hmset).encode().length());
129 |
130 | for (int j = 0; j < 5; j++) {
131 | final long t0 = System.nanoTime();
132 | hmset = Request.cmd(Command.HMSET);
133 |
134 | for (int i = 0; i < iterations; i++) {
135 | hmset.arg(key).arg(i);
136 | }
137 | final long t1 = System.nanoTime();
138 |
139 | System.out.println(((RequestImpl) hmset).encode().length() + "| t " + (t1 - t0));
140 | }
141 | System.out.println("---");
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/internal/CommandNormalizationTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.internal;
2 |
3 | import io.vertx.redis.client.Command;
4 | import io.vertx.redis.client.Request;
5 | import io.vertx.redis.client.impl.CommandImpl;
6 | import io.vertx.redis.client.impl.RequestImpl;
7 | import org.junit.Test;
8 |
9 | import java.nio.charset.StandardCharsets;
10 |
11 | import static org.junit.Assert.assertEquals;
12 | import static org.junit.Assert.assertFalse;
13 | import static org.junit.Assert.assertNotEquals;
14 |
15 | public class CommandNormalizationTest {
16 | @Test
17 | public void test() {
18 | RequestImpl req = (RequestImpl) Request.cmd(Command.create("hset")).arg("key").arg("field").arg("value");
19 | CommandImpl cmd = (CommandImpl) req.command();
20 | assertEquals("hset", cmd.toString());
21 | assertNotEquals(-1, cmd.getArity());
22 | assertFalse(cmd.isReadOnly(req.getArgs()));
23 | assertFalse(cmd.needsGetKeys());
24 | assertEquals(1, req.keys().size());
25 | assertEquals("key", new String(req.keys().get(0), StandardCharsets.UTF_8));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/internal/ConnectionRecyclingTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.internal;
2 |
3 | import io.vertx.core.Vertx;
4 | import io.vertx.core.internal.pool.ConnectionPool;
5 | import io.vertx.core.internal.resource.ResourceManager;
6 | import io.vertx.ext.unit.Async;
7 | import io.vertx.ext.unit.TestContext;
8 | import io.vertx.ext.unit.junit.VertxUnitRunner;
9 | import io.vertx.redis.client.Command;
10 | import io.vertx.redis.client.Redis;
11 | import io.vertx.redis.client.RedisConnection;
12 | import io.vertx.redis.client.RedisOptions;
13 | import io.vertx.redis.client.Request;
14 | import io.vertx.redis.client.impl.RedisClient;
15 | import io.vertx.redis.client.impl.RedisConnectionInternal;
16 | import io.vertx.redis.client.impl.RedisConnectionManager;
17 | import io.vertx.redis.client.impl.RedisConnectionManager.ConnectionKey;
18 | import io.vertx.redis.client.impl.RedisConnectionManager.RedisEndpoint;
19 | import io.vertx.tests.redis.containers.RedisStandalone;
20 | import org.junit.After;
21 | import org.junit.Before;
22 | import org.junit.ClassRule;
23 | import org.junit.Test;
24 | import org.junit.runner.RunWith;
25 |
26 | import java.lang.reflect.Field;
27 |
28 | @RunWith(VertxUnitRunner.class)
29 | public class ConnectionRecyclingTest {
30 |
31 | @ClassRule
32 | public static final RedisStandalone redis = new RedisStandalone();
33 |
34 | Vertx vertx;
35 | Redis client;
36 |
37 | @Before
38 | public void setup() {
39 | vertx = Vertx.vertx();
40 | RedisOptions options = new RedisOptions()
41 | .setConnectionString(redis.getRedisUri())
42 | .setMaxPoolSize(2)
43 | .setPoolCleanerInterval(100)
44 | .setPoolRecycleTimeout(1000);
45 | client = Redis.createClient(vertx, options);
46 | }
47 |
48 | @After
49 | public void teardown(TestContext test) {
50 | client.close();
51 | vertx.close().onComplete(test.asyncAssertSuccess());
52 | }
53 |
54 | @Test
55 | public void testUsageShorterThanRecycleTimeout(TestContext test) {
56 | Async async = test.async();
57 |
58 | assertConnectionPool(test, 0);
59 |
60 | client.connect()
61 | .flatMap(conn -> {
62 | assertConnectionPool(test, 1);
63 | return conn.close();
64 | }).onComplete(test.asyncAssertSuccess(ignored -> {
65 | assertConnectionPool(test, 1);
66 |
67 | vertx.setTimer(2000, ignored2 -> {
68 | assertConnectionPool(test, 0);
69 | async.complete();
70 | });
71 | }));
72 | }
73 |
74 | @Test
75 | public void testUsageLongerThanRecycleTimeout(TestContext test) {
76 | Async async = test.async();
77 |
78 | assertConnectionPool(test, 0);
79 |
80 | client.connect().onComplete(test.asyncAssertSuccess(conn -> {
81 | assertConnectionPool(test, 1);
82 | useConnectionForLongTime(conn, System.currentTimeMillis() + 2000);
83 | }));
84 |
85 | vertx.setTimer(2500, ignored -> {
86 | assertConnectionPool(test, 1);
87 |
88 | vertx.setTimer(1000, ignored2 -> {
89 | assertConnectionPool(test, 0);
90 | async.complete();
91 | });
92 | });
93 | }
94 |
95 | private void useConnectionForLongTime(RedisConnection conn, long endTime) {
96 | if (endTime < System.currentTimeMillis()) {
97 | conn.close();
98 | return;
99 | }
100 |
101 | conn.send(Request.cmd(Command.INFO))
102 | .onSuccess(ignored -> {
103 | vertx.setTimer(100, ignored2 -> {
104 | useConnectionForLongTime(conn, endTime);
105 | });
106 | });
107 | }
108 |
109 | private void assertConnectionPool(TestContext test, int expectedSize) {
110 | IntBox size = new IntBox(0);
111 |
112 | try {
113 | RedisConnectionManager connManager = ((RedisClient) client).connectionManager();
114 | Field field = RedisConnectionManager.class.getDeclaredField("pooledConnectionManager");
115 | field.setAccessible(true);
116 | ResourceManager endpointManager =
117 | (ResourceManager) field.get(connManager);
118 | endpointManager.forEach(endpoint -> {
119 | ConnectionPool pool = ((RedisEndpoint) endpoint).pool();
120 | size.value += pool.size();
121 | });
122 | } catch (ReflectiveOperationException e) {
123 | throw new RuntimeException(e);
124 | }
125 |
126 | test.assertEquals(expectedSize, size.value);
127 | }
128 |
129 | static class IntBox {
130 | int value;
131 |
132 | IntBox(int value) {
133 | this.value = value;
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/internal/RequestImplTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.internal;
2 |
3 | import io.vertx.redis.client.Command;
4 | import io.vertx.redis.client.Request;
5 | import io.vertx.redis.client.impl.RequestImpl;
6 | import org.junit.Test;
7 |
8 | public class RequestImplTest {
9 |
10 | @Test
11 | public void testSimple() {
12 | RequestImpl r = (RequestImpl) Request.cmd(Command.PING);
13 | System.out.println(r.encode());
14 | }
15 |
16 | @Test
17 | public void testWithArgs() {
18 | RequestImpl r = (RequestImpl) Request.cmd(Command.LLEN).arg("mylist");
19 | System.out.println(r.encode());
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/tests/redis/internal/ZModemTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.redis.internal;
2 |
3 | import io.vertx.redis.client.impl.ZModem;
4 | import org.junit.Test;
5 |
6 | import java.util.Arrays;
7 | import java.util.HashSet;
8 | import java.util.Set;
9 |
10 | import static org.junit.Assert.assertEquals;
11 |
12 | public class ZModemTest {
13 |
14 | @Test
15 | public void testGenerate() {
16 | assertEquals(12325, ZModem.generate("foobar"));
17 | assertEquals(9132, ZModem.generate("abcdefghijklmnopqrstuvwxyz"));
18 | assertEquals(15532, ZModem.generate("gsdfhan$%^&*(sdgsdnhshcs"));
19 | assertEquals(12325, ZModem.generate("abc{foobar}"));
20 | assertEquals(12325, ZModem.generate("{foobar}"));
21 | assertEquals(12325, ZModem.generate("h8a9sd{foobar}}{asd}}"));
22 | assertEquals(16235, ZModem.generate("{foobar"));
23 | assertEquals(4435, ZModem.generate("foobar{}"));
24 | assertEquals(16235, ZModem.generate("{{foobar}"));
25 | assertEquals(13690, ZModem.generate("éêe"));
26 | assertEquals(3872, ZModem.generate("àâa"));
27 | assertEquals(14191, ZModem.generate("漢字"));
28 | assertEquals(16196, ZModem.generate("汉字"));
29 | assertEquals(4350, ZModem.generate("호텔"));
30 | assertEquals(9284, ZModem.generate("\uD83D\uDC80"));
31 | assertEquals(11620, ZModem.generate("\uD800\uDC00"));
32 | }
33 |
34 | @Test
35 | public void testGenerateMulti() {
36 | assertEquals(9132, ZModem.generateMulti(Arrays.asList(
37 | "abcdefghijklmnopqrstuvwxyz",
38 | "abcdefghijklmnopqrstuvwxyz",
39 | "abcdefghijklmnopqrstuvwxyz",
40 | "abcdefghijklmnopqrstuvwxyz",
41 | "abcdefghijklmnopqrstuvwxyz",
42 | "abcdefghijklmnopqrstuvwxyz",
43 | "abcdefghijklmnopqrstuvwxyz",
44 | "abcdefghijklmnopqrstuvwxyz"
45 | )));
46 | }
47 |
48 | @Test
49 | public void testAll() {
50 | final Set slots = new HashSet<>();
51 | for (int i = 0; i < Math.pow(2, 17); i++) {
52 | slots.add(ZModem.generate("" + i));
53 | }
54 |
55 | assertEquals(16384, slots.size());
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/test/resources/logging.properties:
--------------------------------------------------------------------------------
1 | handlers=java.util.logging.ConsoleHandler
2 | .level=INFO
3 | java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
4 | java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS,%1$tL %4$-5s [%3$s] %5$s%6$s%n
5 |
--------------------------------------------------------------------------------
/src/test/resources/server-keystore.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vert-x3/vertx-redis-client/c53883492679c8b729a521eb8eefe7652496945d/src/test/resources/server-keystore.jks
--------------------------------------------------------------------------------
/tools/commands.js:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs'
2 | import * as redis from 'redis'
3 | import Handlebars from 'handlebars'
4 | import HandlebarsHelpers from 'handlebars-helpers'
5 |
6 | const excludedPubSub = ['PUBSUB', 'PUBLISH', 'SPUBLISH']
7 |
8 | let client = await redis.createClient().connect()
9 |
10 | let version = null
11 | let infoResponse = await client.INFO()
12 | for (let line of infoResponse.split("\r\n")) {
13 | if (line.startsWith('redis_version')) {
14 | version = line
15 | }
16 | }
17 |
18 | let docs = {}
19 | let commandDocsResponse = await client.sendCommand(['COMMAND', 'DOCS'])
20 | for (let i = 0; i < commandDocsResponse.length; i += 2) {
21 | let name = commandDocsResponse[i]
22 | let docResponse = commandDocsResponse[i + 1]
23 |
24 | let doc = {}
25 | for (let j = 0; j < docResponse.length; j += 2) {
26 | doc[docResponse[j]] = docResponse[j + 1]
27 | }
28 |
29 | let summary = doc['summary'] || null
30 | if (summary != null) {
31 | summary = summary.trim()
32 | if (!summary.endsWith('.')) {
33 | summary += '.'
34 | }
35 | }
36 |
37 | let deprecated = false
38 | if (doc['doc_flags']) {
39 | for (let flag of doc['doc_flags']) {
40 | if (flag === 'deprecated') {
41 | deprecated = true
42 | break
43 | }
44 | }
45 | }
46 | let deprecatedSince = null
47 | let replacedBy = null
48 | if (deprecated) {
49 | deprecatedSince = doc['deprecated_since'] || 'unknown'
50 | replacedBy = doc['replaced_by'] || 'unknown'
51 | replacedBy = replacedBy.replaceAll(/`(.*?)`/g, '{@code $1}')
52 | }
53 |
54 | docs[name] = {
55 | summary,
56 | deprecatedSince,
57 | replacedBy
58 | }
59 | }
60 |
61 | let commands = []
62 | let commandInfoResponse = await client.sendCommand(['COMMAND', 'INFO'])
63 | commandInfoResponse.sort((a, b) => {
64 | return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0
65 | })
66 | for (let cmd of commandInfoResponse) {
67 | let types = ""
68 | let args = ""
69 | let argLen = 0
70 |
71 | let identifier = cmd[0].replace('.', '_').replace('-', '_').replace(':', '').toUpperCase()
72 |
73 | if (cmd[1] > 0) {
74 | for (let i = 0; i < cmd[1] - 1; i++) {
75 | if (i !== 0) {
76 | args += ', '
77 | types += ', '
78 | }
79 | types += ("String arg" + i)
80 | args += ("arg" + i)
81 | }
82 | // arg len includes the command name
83 | argLen = cmd[1]
84 | if (argLen) {
85 | argLen--
86 | }
87 | }
88 |
89 | if (cmd[1] < 0) {
90 | types = "List args"
91 | args = "args"
92 | argLen = Math.abs(cmd[1])
93 | }
94 |
95 | commands.push({
96 | enum: identifier,
97 | name: cmd[0],
98 | safename: cmd[0].replace('-', ' ').replace(':', '').toUpperCase(),
99 | arity: cmd[1],
100 | variable: cmd[1] < 0,
101 | argLen: argLen,
102 | args: args,
103 | types: types,
104 | firstKey: cmd[3],
105 | lastKey: cmd[4],
106 | multiKey: cmd[4] < 0,
107 | interval: cmd[5],
108 | keyless: cmd[5] === 0 && cmd[2].indexOf('movablekeys') === -1,
109 | write: cmd[2].indexOf('write') !== -1,
110 | readOnly: cmd[2].indexOf('readonly') !== -1,
111 | movable: cmd[2].indexOf('movablekeys') !== -1,
112 | pubsub: cmd[2].indexOf('pubsub') !== -1 && !excludedPubSub.includes(identifier),
113 | docsSummary: docs[cmd[0]].summary,
114 | docsDeprecatedSince: docs[cmd[0]].deprecatedSince,
115 | docsReplacedBy: docs[cmd[0]].replacedBy,
116 | })
117 | }
118 |
119 | await client.disconnect()
120 |
121 | HandlebarsHelpers()
122 | let template = Handlebars.compile(fs.readFileSync('redis-api.hbs', 'utf8'))
123 | let rendered = template({
124 | commands,
125 | version,
126 | })
127 | fs.writeFileSync('../src/main/java/io/vertx/redis/client/RedisAPI.java', rendered)
128 |
--------------------------------------------------------------------------------
/tools/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "commands-generator",
3 | "private": true,
4 | "type": "module",
5 | "main": "commands.js",
6 | "dependencies": {
7 | "handlebars": "^4.7.8",
8 | "handlebars-helpers": "^0.10.0",
9 | "redis": "^4.7.0"
10 | },
11 | "scripts": {
12 | "--prestart": "docker run --rm --net=host redis/redis-stack-server:7.0.6-RC9",
13 | "start": "node commands.js"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/tools/redis-api.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 | package io.vertx.redis.client;
17 |
18 | import io.vertx.codegen.annotations.GenIgnore;
19 | import io.vertx.codegen.annotations.Nullable;
20 | import io.vertx.codegen.annotations.VertxGen;
21 | import io.vertx.core.Future;
22 | import io.vertx.redis.client.impl.RedisAPIImpl;
23 |
24 | import java.util.List;
25 |
26 | import static io.vertx.codegen.annotations.GenIgnore.PERMITTED_TYPE;
27 |
28 | /**
29 | * Auto generated Redis API client wrapper.
30 | * @version {{ version }}
31 | */
32 | @VertxGen
33 | public interface RedisAPI {
34 |
35 | @GenIgnore(PERMITTED_TYPE)
36 | static RedisAPI api(Redis client) {
37 | return new RedisAPIImpl(client);
38 | }
39 |
40 | @GenIgnore(PERMITTED_TYPE)
41 | static RedisAPI api(RedisConnection connection) {
42 | return new RedisAPIImpl(connection);
43 | }
44 |
45 | void close();
46 | {{#each commands}}
47 |
48 | /**
49 | {{#if docsSummary }}
50 | * {{docsSummary}}
51 | *
52 | {{/if}}
53 | * Redis command {{ uppercase name }}.
54 | * @return Future response.
55 | {{#if docsDeprecatedSince}}
56 | * @deprecated since: {{docsDeprecatedSince}}, replaced by: {{docsReplacedBy}}
57 | {{/if}}
58 | */
59 | {{#if docsDeprecatedSince}}
60 | @Deprecated
61 | {{/if}}
62 | default Future<@Nullable Response> {{ camelcase safename }}({{{ types }}}) {
63 | return send(Command.{{ enum }}{{#if argLen}}, {{{ args }}}{{#if variable}}.toArray(new String[0]){{/if}}{{/if}});
64 | }
65 | {{/each}}
66 |
67 | /**
68 | * Send untyped command to redis.
69 | *
70 | * @param cmd the command
71 | * @param args var args
72 | * @return Future response.
73 | */
74 | @GenIgnore
75 | Future<@Nullable Response> send(Command cmd, String... args);
76 | }
77 |
--------------------------------------------------------------------------------