diff --git a/src/main/java/io/vertx/redis/client/impl/RedisClusterClient.java b/src/main/java/io/vertx/redis/client/impl/RedisClusterClient.java index 77a11c26..6fd46d75 100644 --- a/src/main/java/io/vertx/redis/client/impl/RedisClusterClient.java +++ b/src/main/java/io/vertx/redis/client/impl/RedisClusterClient.java @@ -226,16 +226,20 @@ private Future getSlots(ContextInternal context) { if (this.slots.compareAndSet(null, future)) { LOG.debug("Obtaining hash slot assignment"); // attempt to load the slots from the first good endpoint - getSlots(connectOptions.getEndpoints(), 0, promise); + getSlots(connectOptions.getEndpoints(), 0, ConcurrentHashMap.newKeySet(), promise); return future; } } } - private void getSlots(List endpoints, int index, Handler> onGotSlots) { + private void getSlots(List endpoints, int index, Set failures, Handler> onGotSlots) { if (index >= endpoints.size()) { // stop condition - onGotSlots.handle(Future.failedFuture(new RedisConnectException("Cannot connect to any of the provided endpoints"))); + StringBuilder message = new StringBuilder("Cannot connect to any of the provided endpoints"); + for (Throwable failure : failures) { + message.append("\n- ").append(failure); + } + onGotSlots.handle(Future.failedFuture(new RedisConnectException(message.toString()))); scheduleCachedSlotsExpiration(); return; } @@ -243,7 +247,8 @@ private void getSlots(List endpoints, int index, Handler { // try with the next endpoint - getSlots(endpoints, index + 1, onGotSlots); + failures.add(err); + getSlots(endpoints, index + 1, failures, onGotSlots); }) .onSuccess(conn -> { getSlots(endpoints.get(index), conn).onComplete(result -> { @@ -253,7 +258,8 @@ private void getSlots(List endpoints, int index, Handler getSlots(String endpoint, RedisConnection conn) { .compose(reply -> { if (reply == null || reply.size() == 0) { // no slots available we can't really proceed - return Future.failedFuture("SLOTS No slots available in the cluster."); + return Future.failedFuture("CLUSTER SLOTS No slots available in the cluster."); } - return Future.succeededFuture(new Slots(endpoint, reply)); + Slots result; + try { + result = new Slots(endpoint, reply); + } catch (Exception e) { + return Future.failedFuture("CLUSTER SLOTS response invalid: " + e); + } + return Future.succeededFuture(result); }); } diff --git a/src/main/java/io/vertx/redis/client/impl/RedisReplicationClient.java b/src/main/java/io/vertx/redis/client/impl/RedisReplicationClient.java index b01a42c9..ca5e003a 100644 --- a/src/main/java/io/vertx/redis/client/impl/RedisReplicationClient.java +++ b/src/main/java/io/vertx/redis/client/impl/RedisReplicationClient.java @@ -24,6 +24,7 @@ import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import static io.vertx.redis.client.Command.*; @@ -109,21 +110,26 @@ public Future connect() { // make a copy as we may need to mutate the list during discovery final List endpoints = new LinkedList<>(connectOptions.getEndpoints()); // attempt to discover the topology from the first good endpoint - connect(endpoints, 0, promise); + connect(endpoints, 0, ConcurrentHashMap.newKeySet(), promise); return promise.future(); } - private void connect(List endpoints, int index, Handler> onConnect) { + private void connect(List endpoints, int index, Set failures, Handler> onConnect) { if (index >= endpoints.size()) { // stop condition - onConnect.handle(Future.failedFuture(new RedisConnectException("Cannot connect to any of the provided endpoints"))); + StringBuilder message = new StringBuilder("Cannot connect to any of the provided endpoints"); + for (Throwable failure : failures) { + message.append("\n- ").append(failure); + } + onConnect.handle(Future.failedFuture(new RedisConnectException(message.toString()))); return; } connectionManager.getConnection(endpoints.get(index), null) .onFailure(err -> { // failed try with the next endpoint - connect(endpoints, index + 1, onConnect); + failures.add(err); + connect(endpoints, index + 1, failures, onConnect); }) .onSuccess(conn -> { // fetch slots from the cluster immediately to ensure slots are correct @@ -132,7 +138,8 @@ private void connect(List endpoints, int index, Handler> callback) { // Because finding the master is going to be an async list we will terminate // when we find one then use promises... - iterate(0, checkEndpointFn, options, iterate -> { + iterate(0, ConcurrentHashMap.newKeySet(), checkEndpointFn, options, iterate -> { if (iterate.failed()) { callback.handle(Future.failedFuture(iterate.cause())); } else { @@ -175,12 +177,16 @@ private static void resolveClient(final Resolver checkEndpointFn, final RedisSen }); } - private static void iterate(final int idx, final Resolver checkEndpointFn, final RedisSentinelConnectOptions argument, final Handler>> resultHandler) { + private static void iterate(final int idx, final Set failures, final Resolver checkEndpointFn, final RedisSentinelConnectOptions argument, final Handler>> resultHandler) { // stop condition final List endpoints = argument.getEndpoints(); if (idx >= endpoints.size()) { - resultHandler.handle(Future.failedFuture("No more endpoints in chain.")); + StringBuilder message = new StringBuilder("Cannot connect to any of the provided endpoints"); + for (Throwable failure : failures) { + message.append("\n- ").append(failure); + } + resultHandler.handle(Future.failedFuture(new RedisConnectException(message.toString()))); return; } @@ -190,7 +196,8 @@ private static void iterate(final int idx, final Resolver checkEndpointFn, final resultHandler.handle(Future.succeededFuture(new Pair<>(idx, res.result()))); } else { // try again with next endpoint - iterate(idx + 1, checkEndpointFn, argument, resultHandler); + failures.add(res.cause()); + iterate(idx + 1, failures, checkEndpointFn, argument, resultHandler); } }); }