/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ignite.internal.client; import java.nio.channels.ClosedChannelException; import java.util.ArrayList; import java.util.Collection; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.client.impl.connection.GridClientConnectionResetException; import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import org.apache.ignite.testsuites.IgniteIgnore; /** * */ public class ClientReconnectionSelfTest extends GridCommonAbstractTest { /** */ public static final String HOST = "127.0.0.1"; /** */ private ClientTestRestServer[] srvs = new ClientTestRestServer[ClientTestRestServer.SERVERS_CNT]; /** {@inheritDoc} */ @Override protected void afterTest() throws Exception { for (int i = 0; i < srvs.length; i++) { ClientTestRestServer srv = srvs[i]; if (srv != null) srv.stop(); srvs[i] = null; } super.afterTest(); } /** * @return Client for test. * @throws GridClientException In case of error. */ private GridClient client() throws GridClientException { return client(HOST); } /** * @param host - server host * @return Client for test. * @throws GridClientException In case of error. */ private GridClient client(String host) throws GridClientException { GridClientConfiguration cfg = new GridClientConfiguration(); cfg.setProtocol(GridClientProtocol.TCP); Collection<String> addrs = new ArrayList<>(); for (int port = ClientTestRestServer.FIRST_SERVER_PORT; port < ClientTestRestServer.FIRST_SERVER_PORT + ClientTestRestServer.SERVERS_CNT; port++) addrs.add(host + ":" + port); cfg.setServers(addrs); cfg.setTopologyRefreshFrequency(60 * 60 * 1000); return GridClientFactory.start(cfg); } /** * @throws Exception If failed. */ public void testNoFailedReconnection() throws Exception { for (int i = 0; i < ClientTestRestServer.SERVERS_CNT; i++) runServer(i, false); try (GridClient client = client()) { // Here client opens initial connection and fetches topology. // Only first server in list should be contacted. assertEquals(1, srvs[0].getConnectCount()); for (int i = 1; i < ClientTestRestServer.SERVERS_CNT; i++) assertEquals(0, srvs[i].getConnectCount()); srvs[0].resetCounters(); int contactedSrv = 0; for (int i = 0; i < 100; i++) { int failed = contactedSrv; srvs[failed].fail(); // Sometimes session close missing on client side. Retry few times until request succeeds. while (true) try { client.compute().refreshTopology(false, false); break; } catch (GridClientConnectionResetException e) { info("Exception caught: " + e); } // Check which servers where contacted, int connects = 0; for (int srv = 0; srv < ClientTestRestServer.SERVERS_CNT; srv++) { if (srvs[srv].getSuccessfulConnectCount() > 0) { assertTrue("Failed server was contacted: " + srv, srv != failed); contactedSrv = srv; } connects += srvs[srv].getSuccessfulConnectCount(); } assertEquals(1, connects); // Only one new connection should be opened. srvs[failed].repair(); srvs[contactedSrv].resetCounters(); // It should be the only server with non-0 counters. } } } /** * @throws Exception If failed. */ public void testCorrectInit() throws Exception { for (int i = 0; i < ClientTestRestServer.SERVERS_CNT; i++) runServer(i, i == 0); try (GridClient ignored = client()) { // Here client opens initial connection and fetches topology. // First and second should be contacted, due to failure in initial request to the first. for (int i = 0; i < 2; i++) assertEquals("Iteration: " + i, 1, srvs[i].getConnectCount()); for (int i = 2; i < ClientTestRestServer.SERVERS_CNT; i++) assertEquals(0, srvs[i].getConnectCount()); } } /** * @throws Exception If failed. */ public void testFailedInit() throws Exception { for (int i = 0; i < ClientTestRestServer.SERVERS_CNT; i++) runServer(i, true); GridClient c = client(); try { c.compute().execute("fake", "arg"); fail("Client operation should fail when server resets connections."); } catch (GridClientDisconnectedException e) { assertTrue("Thrown exception doesn't have an expected cause: " + X.getFullStackTrace(e), X.hasCause(e, GridClientConnectionResetException.class, ClosedChannelException.class)); } for (int i = 0; i < ClientTestRestServer.SERVERS_CNT; i++) // Connection manager does 3 attempts to get topology before failure. assertEquals("Server: " + i, 3, srvs[i].getConnectCount()); } /** * TODO: IGNITE-590. * * @throws Exception If failed. */ @IgniteIgnore(value = "https://issues.apache.org/jira/browse/IGNITE-590", forceFailure = true) public void testIdleConnection() throws Exception { int srvsCnt = 4; // TODO: IGNITE-590 it may be wrong value. Need to investigate after IGNITE-590 will be fixed. for (int i = 0; i < srvsCnt; i++) runServer(i, false); GridClient client = client(); // Here client opens initial connection and fetches topology. try { // Only first server in list should be contacted. assertEquals(1, srvs[0].getConnectCount()); Thread.sleep(35000); // Timeout as idle. assertEquals(1, srvs[0].getDisconnectCount()); for (int i = 1; i < srvsCnt; i++) assertEquals(0, srvs[i].getConnectCount()); srvs[0].resetCounters(); // On new request connection should be re-opened. client.compute().refreshTopology(false, false); assertEquals(1, srvs[0].getConnectCount()); for (int i = 1; i < srvsCnt; i++) assertEquals(0, srvs[i].getConnectCount()); } finally { GridClientFactory.stop(client.id()); } } /** * Runs a new server with given index. * * @param idx Server index, same as in client configuration's servers property. * @param failOnConnect If {@code true} the server should fail incoming connection immediately. * @return Server instance. * @throws IgniteCheckedException If failed. */ private ClientTestRestServer runServer(int idx, boolean failOnConnect) throws IgniteCheckedException { ClientTestRestServer srv = new ClientTestRestServer(ClientTestRestServer.FIRST_SERVER_PORT + idx, failOnConnect, log()); srv.start(); srvs[idx] = srv; return srv; } }