|
@@ -55,7 +55,9 @@ import java.util.HashSet;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.Set;
|
|
|
+import java.util.concurrent.CountDownLatch;
|
|
|
import java.util.concurrent.CyclicBarrier;
|
|
|
+import java.util.concurrent.Semaphore;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
@@ -97,9 +99,7 @@ public class NodeConnectionsServiceTests extends ESTestCase {
|
|
|
final AtomicBoolean keepGoing = new AtomicBoolean(true);
|
|
|
final Thread reconnectionThread = new Thread(() -> {
|
|
|
while (keepGoing.get()) {
|
|
|
- final PlainActionFuture<Void> future = new PlainActionFuture<>();
|
|
|
- service.ensureConnections(() -> future.onResponse(null));
|
|
|
- future.actionGet();
|
|
|
+ ensureConnections(service);
|
|
|
}
|
|
|
}, "reconnection thread");
|
|
|
reconnectionThread.start();
|
|
@@ -109,34 +109,18 @@ public class NodeConnectionsServiceTests extends ESTestCase {
|
|
|
final boolean isDisrupting = randomBoolean();
|
|
|
final Thread disruptionThread = new Thread(() -> {
|
|
|
while (isDisrupting && keepGoing.get()) {
|
|
|
- final Transport.Connection connection;
|
|
|
- try {
|
|
|
- connection = transportService.getConnection(randomFrom(allNodes));
|
|
|
- } catch (NodeNotConnectedException e) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- final PlainActionFuture<Void> future = new PlainActionFuture<>();
|
|
|
- connection.addRemovedListener(future);
|
|
|
- connection.close();
|
|
|
- future.actionGet(10, TimeUnit.SECONDS);
|
|
|
+ closeConnection(transportService, randomFrom(allNodes));
|
|
|
}
|
|
|
}, "disruption thread");
|
|
|
disruptionThread.start();
|
|
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
- final DiscoveryNodes connectNodes = discoveryNodesFromList(randomSubsetOf(allNodes));
|
|
|
- final PlainActionFuture<Void> future = new PlainActionFuture<>();
|
|
|
- service.connectToNodes(connectNodes, () -> future.onResponse(null));
|
|
|
- future.actionGet(10, TimeUnit.SECONDS);
|
|
|
- final DiscoveryNodes disconnectExceptNodes = discoveryNodesFromList(randomSubsetOf(allNodes));
|
|
|
- service.disconnectFromNodesExcept(disconnectExceptNodes);
|
|
|
+ connectToNodes(service, discoveryNodesFromList(randomSubsetOf(allNodes)));
|
|
|
+ service.disconnectFromNodesExcept(discoveryNodesFromList(randomSubsetOf(allNodes)));
|
|
|
}
|
|
|
|
|
|
final DiscoveryNodes nodes = discoveryNodesFromList(randomSubsetOf(allNodes));
|
|
|
- final PlainActionFuture<Void> connectFuture = new PlainActionFuture<>();
|
|
|
- service.connectToNodes(nodes, () -> connectFuture.onResponse(null));
|
|
|
- connectFuture.actionGet(10, TimeUnit.SECONDS);
|
|
|
+ connectToNodes(service, nodes);
|
|
|
service.disconnectFromNodesExcept(nodes);
|
|
|
|
|
|
assertTrue(keepGoing.compareAndSet(true, false));
|
|
@@ -144,14 +128,59 @@ public class NodeConnectionsServiceTests extends ESTestCase {
|
|
|
disruptionThread.join();
|
|
|
|
|
|
if (isDisrupting) {
|
|
|
- final PlainActionFuture<Void> ensureFuture = new PlainActionFuture<>();
|
|
|
- service.ensureConnections(() -> ensureFuture.onResponse(null));
|
|
|
- ensureFuture.actionGet(10, TimeUnit.SECONDS);
|
|
|
+ ensureConnections(service);
|
|
|
}
|
|
|
|
|
|
+ assertConnected(transportService, nodes);
|
|
|
assertBusy(() -> assertConnectedExactlyToNodes(nodes));
|
|
|
}
|
|
|
|
|
|
+ public void testConcurrentConnectAndDisconnect() throws Exception {
|
|
|
+ final NodeConnectionsService service = new NodeConnectionsService(Settings.EMPTY, threadPool, transportService);
|
|
|
+
|
|
|
+ final AtomicBoolean keepGoing = new AtomicBoolean(true);
|
|
|
+ final Thread reconnectionThread = new Thread(() -> {
|
|
|
+ while (keepGoing.get()) {
|
|
|
+ ensureConnections(service);
|
|
|
+ }
|
|
|
+ }, "reconnection thread");
|
|
|
+ reconnectionThread.start();
|
|
|
+
|
|
|
+ final var node = new DiscoveryNode("node", buildNewFakeTransportAddress(), Map.of(), Set.of(), Version.CURRENT);
|
|
|
+ final var nodes = discoveryNodesFromList(List.of(node));
|
|
|
+
|
|
|
+ final Thread disruptionThread = new Thread(() -> {
|
|
|
+ while (keepGoing.get()) {
|
|
|
+ closeConnection(transportService, node);
|
|
|
+ }
|
|
|
+ }, "disruption thread");
|
|
|
+ disruptionThread.start();
|
|
|
+
|
|
|
+ final var reconnectPermits = new Semaphore(1000);
|
|
|
+ final var reconnectThreads = 10;
|
|
|
+ final var reconnectCountDown = new CountDownLatch(reconnectThreads);
|
|
|
+ for (int i = 0; i < reconnectThreads; i++) {
|
|
|
+ threadPool.generic().execute(new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ if (reconnectPermits.tryAcquire()) {
|
|
|
+ service.connectToNodes(nodes, () -> threadPool.generic().execute(this));
|
|
|
+ } else {
|
|
|
+ reconnectCountDown.countDown();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ assertTrue(reconnectCountDown.await(10, TimeUnit.SECONDS));
|
|
|
+ assertTrue(keepGoing.compareAndSet(true, false));
|
|
|
+ reconnectionThread.join();
|
|
|
+ disruptionThread.join();
|
|
|
+
|
|
|
+ ensureConnections(service);
|
|
|
+ assertConnectedExactlyToNodes(nodes);
|
|
|
+ }
|
|
|
+
|
|
|
public void testPeriodicReconnection() {
|
|
|
final Settings.Builder settings = Settings.builder();
|
|
|
final long reconnectIntervalMillis;
|
|
@@ -234,30 +263,24 @@ public class NodeConnectionsServiceTests extends ESTestCase {
|
|
|
// connect to one node
|
|
|
final DiscoveryNode node0 = new DiscoveryNode("node0", buildNewFakeTransportAddress(), Version.CURRENT);
|
|
|
final DiscoveryNodes nodes0 = DiscoveryNodes.builder().add(node0).build();
|
|
|
- final PlainActionFuture<Void> future0 = new PlainActionFuture<>();
|
|
|
- service.connectToNodes(nodes0, () -> future0.onResponse(null));
|
|
|
- future0.actionGet(10, TimeUnit.SECONDS);
|
|
|
+ connectToNodes(service, nodes0);
|
|
|
assertConnectedExactlyToNodes(nodes0);
|
|
|
|
|
|
// connection attempts to node0 block indefinitely
|
|
|
final CyclicBarrier connectionBarrier = new CyclicBarrier(2);
|
|
|
try {
|
|
|
- nodeConnectionBlocks.put(node0, connectionBarrier::await);
|
|
|
+ nodeConnectionBlocks.put(node0, () -> connectionBarrier.await(10, TimeUnit.SECONDS));
|
|
|
transportService.disconnectFromNode(node0);
|
|
|
|
|
|
// can still connect to another node without blocking
|
|
|
final DiscoveryNode node1 = new DiscoveryNode("node1", buildNewFakeTransportAddress(), Version.CURRENT);
|
|
|
final DiscoveryNodes nodes1 = DiscoveryNodes.builder().add(node1).build();
|
|
|
final DiscoveryNodes nodes01 = DiscoveryNodes.builder(nodes0).add(node1).build();
|
|
|
- final PlainActionFuture<Void> future1 = new PlainActionFuture<>();
|
|
|
- service.connectToNodes(nodes01, () -> future1.onResponse(null));
|
|
|
- future1.actionGet(10, TimeUnit.SECONDS);
|
|
|
+ connectToNodes(service, nodes01);
|
|
|
assertConnectedExactlyToNodes(nodes1);
|
|
|
|
|
|
// can also disconnect from node0 without blocking
|
|
|
- final PlainActionFuture<Void> future2 = new PlainActionFuture<>();
|
|
|
- service.connectToNodes(nodes1, () -> future2.onResponse(null));
|
|
|
- future2.actionGet(10, TimeUnit.SECONDS);
|
|
|
+ connectToNodes(service, nodes1);
|
|
|
service.disconnectFromNodesExcept(nodes1);
|
|
|
assertConnectedExactlyToNodes(nodes1);
|
|
|
|
|
@@ -273,17 +296,15 @@ public class NodeConnectionsServiceTests extends ESTestCase {
|
|
|
|
|
|
// the reconnection is also blocked but the connection future doesn't wait, it completes straight away
|
|
|
transportService.disconnectFromNode(node0);
|
|
|
- final PlainActionFuture<Void> future4 = new PlainActionFuture<>();
|
|
|
- service.connectToNodes(nodes01, () -> future4.onResponse(null));
|
|
|
- future4.actionGet(10, TimeUnit.SECONDS);
|
|
|
+ connectToNodes(service, nodes01);
|
|
|
assertConnectedExactlyToNodes(nodes1);
|
|
|
|
|
|
// a blocked reconnection attempt doesn't also block the node from being deregistered
|
|
|
service.disconnectFromNodesExcept(nodes1);
|
|
|
- final PlainActionFuture<DiscoveryNode> disconnectFuture1 = new PlainActionFuture<>();
|
|
|
- assertTrue(disconnectListenerRef.compareAndSet(null, disconnectFuture1));
|
|
|
- connectionBarrier.await();
|
|
|
- assertThat(disconnectFuture1.actionGet(10, TimeUnit.SECONDS), equalTo(node0)); // node0 connects briefly, must wait here
|
|
|
+ assertThat(PlainActionFuture.get(disconnectFuture1 -> {
|
|
|
+ assertTrue(disconnectListenerRef.compareAndSet(null, disconnectFuture1));
|
|
|
+ connectionBarrier.await(10, TimeUnit.SECONDS);
|
|
|
+ }, 10, TimeUnit.SECONDS), equalTo(node0)); // node0 connects briefly, must wait here
|
|
|
assertConnectedExactlyToNodes(nodes1);
|
|
|
|
|
|
// a blocked connection attempt to a new node also doesn't prevent an immediate deregistration
|
|
@@ -294,10 +315,10 @@ public class NodeConnectionsServiceTests extends ESTestCase {
|
|
|
service.disconnectFromNodesExcept(nodes1);
|
|
|
assertConnectedExactlyToNodes(nodes1);
|
|
|
|
|
|
- final PlainActionFuture<DiscoveryNode> disconnectFuture2 = new PlainActionFuture<>();
|
|
|
- assertTrue(disconnectListenerRef.compareAndSet(null, disconnectFuture2));
|
|
|
- connectionBarrier.await(10, TimeUnit.SECONDS);
|
|
|
- assertThat(disconnectFuture2.actionGet(10, TimeUnit.SECONDS), equalTo(node0)); // node0 connects briefly, must wait here
|
|
|
+ assertThat(PlainActionFuture.get(disconnectFuture2 -> {
|
|
|
+ assertTrue(disconnectListenerRef.compareAndSet(null, disconnectFuture2));
|
|
|
+ connectionBarrier.await(10, TimeUnit.SECONDS);
|
|
|
+ }, 10, TimeUnit.SECONDS), equalTo(node0)); // node0 connects briefly, must wait here
|
|
|
assertConnectedExactlyToNodes(nodes1);
|
|
|
assertTrue(future5.isDone());
|
|
|
} finally {
|
|
@@ -310,7 +331,7 @@ public class NodeConnectionsServiceTests extends ESTestCase {
|
|
|
reason = "testing that DEBUG-level logging is reasonable",
|
|
|
value = "org.elasticsearch.cluster.NodeConnectionsService:DEBUG"
|
|
|
)
|
|
|
- public void testDebugLogging() throws IllegalAccessException {
|
|
|
+ public void testDebugLogging() {
|
|
|
final DeterministicTaskQueue deterministicTaskQueue = new DeterministicTaskQueue();
|
|
|
|
|
|
MockTransport transport = new MockTransport(deterministicTaskQueue.getThreadPool());
|
|
@@ -706,4 +727,23 @@ public class NodeConnectionsServiceTests extends ESTestCase {
|
|
|
return requestHandlers;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ private static void connectToNodes(NodeConnectionsService service, DiscoveryNodes discoveryNodes) {
|
|
|
+ PlainActionFuture.get(future -> service.connectToNodes(discoveryNodes, () -> future.onResponse(null)), 10, TimeUnit.SECONDS);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void ensureConnections(NodeConnectionsService service) {
|
|
|
+ PlainActionFuture.get(future -> service.ensureConnections(() -> future.onResponse(null)), 10, TimeUnit.SECONDS);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void closeConnection(TransportService transportService, DiscoveryNode discoveryNode) {
|
|
|
+ try {
|
|
|
+ final var connection = transportService.getConnection(discoveryNode);
|
|
|
+ connection.close();
|
|
|
+ PlainActionFuture.get(connection::addRemovedListener, 10, TimeUnit.SECONDS);
|
|
|
+ } catch (NodeNotConnectedException e) {
|
|
|
+ // ok
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|