/* * 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.spi.discovery.tcp; import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.spi.IgniteSpiOperationTimeoutException; import org.apache.ignite.spi.IgniteSpiOperationTimeoutHelper; import org.apache.ignite.spi.discovery.AbstractDiscoverySelfTest; import org.apache.ignite.spi.discovery.DiscoverySpi; import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode; import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage; import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryConnectionCheckMessage; import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryPingRequest; /** * */ public class TcpDiscoverySpiFailureTimeoutSelfTest extends AbstractDiscoverySelfTest { /** */ private static final int SPI_COUNT = 6; /** */ private TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true); /** {@inheritDoc} */ @Override protected int getSpiCount() { return SPI_COUNT; } /** {@inheritDoc} */ @Override protected DiscoverySpi getSpi(int idx) { TestTcpDiscoverySpi spi = new TestTcpDiscoverySpi(); spi.setMetricsProvider(createMetricsProvider()); spi.setIpFinder(ipFinder); switch (idx) { case 0: case 1: // Ignore break; case 2: spi.setAckTimeout(3000); break; case 3: spi.setSocketTimeout(4000); break; case 4: spi.setReconnectCount(4); break; case 5: spi.setMaxAckTimeout(10000); break; default: assert false; } return spi; } /** * @throws Exception In case of error. */ public void testFailureDetectionTimeoutEnabled() throws Exception { assertTrue(firstSpi().failureDetectionTimeoutEnabled()); assertTrue(secondSpi().failureDetectionTimeoutEnabled()); assertEquals(IgniteConfiguration.DFLT_FAILURE_DETECTION_TIMEOUT.longValue(), firstSpi().failureDetectionTimeout()); assertEquals(IgniteConfiguration.DFLT_FAILURE_DETECTION_TIMEOUT.longValue(), secondSpi().failureDetectionTimeout()); assertEquals(IgniteConfiguration.DFLT_CLIENT_FAILURE_DETECTION_TIMEOUT.longValue(), firstSpi().clientFailureDetectionTimeout()); assertEquals(IgniteConfiguration.DFLT_CLIENT_FAILURE_DETECTION_TIMEOUT.longValue(), secondSpi().clientFailureDetectionTimeout()); } /** * @throws Exception In case of error. */ public void testFailureDetectionTimeoutDisabled() throws Exception { for (int i = 2; i < spis.size(); i++) { assertFalse(((TcpDiscoverySpi)spis.get(i)).failureDetectionTimeoutEnabled()); assertEquals(0, ((TcpDiscoverySpi)spis.get(i)).failureDetectionTimeout()); assertFalse(0 == ((TcpDiscoverySpi)spis.get(i)).clientFailureDetectionTimeout()); } } /** * @throws Exception In case of error. */ public void testFailureDetectionOnSocketOpen() throws Exception { try { ClusterNode node = secondSpi().getLocalNode(); firstSpi().openSockTimeout = true; assertFalse(firstSpi().pingNode(node.id())); assertTrue(firstSpi().validTimeout); assertTrue(firstSpi().err.getMessage().equals("Timeout: openSocketTimeout")); firstSpi().openSockTimeout = false; firstSpi().openSockTimeoutWait = true; assertFalse(firstSpi().pingNode(node.id())); assertTrue(firstSpi().validTimeout); assertTrue(firstSpi().err.getMessage().equals("Timeout: openSocketTimeoutWait")); } finally { firstSpi().resetState(); } } /** * @throws Exception In case of error. */ public void testFailureDetectionOnSocketWrite() throws Exception { try { ClusterNode node = secondSpi().getLocalNode(); firstSpi().writeToSockTimeoutWait = true; assertFalse(firstSpi().pingNode(node.id())); assertTrue(firstSpi().validTimeout); firstSpi().writeToSockTimeoutWait = false; assertTrue(firstSpi().pingNode(node.id())); assertTrue(firstSpi().validTimeout); } finally { firstSpi().resetState(); } } /** * @throws Exception In case of error. */ public void testConnectionCheckMessage() throws Exception { TestTcpDiscoverySpi nextSpi = null; try { assert firstSpi().connCheckStatusMsgCntSent == 0; TcpDiscoveryNode nextNode = ((ServerImpl)(firstSpi().impl)).ring().nextNode(); assertNotNull(nextNode); nextSpi = null; for (int i = 1; i < spis.size(); i++) if (spis.get(i).getLocalNode().id().equals(nextNode.id())) { nextSpi = (TestTcpDiscoverySpi)spis.get(i); break; } assertNotNull(nextSpi); assert nextSpi.connCheckStatusMsgCntReceived == 0; firstSpi().cntConnCheckMsg = true; nextSpi.cntConnCheckMsg = true; Thread.sleep(firstSpi().failureDetectionTimeout()); firstSpi().cntConnCheckMsg = false; nextSpi.cntConnCheckMsg = false; int sent = firstSpi().connCheckStatusMsgCntSent; int received = nextSpi.connCheckStatusMsgCntReceived; assert sent >= 3 && sent < 7 : "messages sent: " + sent; assert received >= 3 && received < 7 : "messages received: " + received; } finally { firstSpi().resetState(); if (nextSpi != null) nextSpi.resetState(); } } /** * Returns the first spi with failure detection timeout enabled. * * @return SPI. */ private TestTcpDiscoverySpi firstSpi() { return (TestTcpDiscoverySpi)spis.get(0); } /** * Returns the second spi with failure detection timeout enabled. * * @return SPI. */ private TestTcpDiscoverySpi secondSpi() { return (TestTcpDiscoverySpi)spis.get(1); } /** * */ private static class TestTcpDiscoverySpi extends TcpDiscoverySpi { /** */ private volatile boolean openSockTimeout; /** */ private volatile boolean openSockTimeoutWait; /** */ private volatile boolean writeToSockTimeoutWait; /** */ private volatile boolean cntConnCheckMsg; /** */ private volatile int connCheckStatusMsgCntSent; /** */ private volatile int connCheckStatusMsgCntReceived; /** */ private volatile boolean validTimeout = true; /** */ private volatile IgniteSpiOperationTimeoutException err; /** {@inheritDoc} */ @Override protected Socket openSocket(Socket sock, InetSocketAddress sockAddr, IgniteSpiOperationTimeoutHelper timeoutHelper) throws IOException, IgniteSpiOperationTimeoutException { if (openSockTimeout) { err = new IgniteSpiOperationTimeoutException("Timeout: openSocketTimeout"); throw err; } else if (openSockTimeoutWait) { long timeout = timeoutHelper.nextTimeoutChunk(0); try { Thread.sleep(timeout + 1000); } catch (InterruptedException ignored) { // No-op. } try { timeoutHelper.nextTimeoutChunk(0); } catch (IgniteSpiOperationTimeoutException ignored) { throw (err = new IgniteSpiOperationTimeoutException("Timeout: openSocketTimeoutWait")); } } super.openSocket(sock, sockAddr, timeoutHelper); try { Thread.sleep(1500); } catch (InterruptedException ignored) { // No-op. } return sock; } /** {@inheritDoc} */ @Override protected void writeToSocket(Socket sock, OutputStream out, TcpDiscoveryAbstractMessage msg, long timeout) throws IOException, IgniteCheckedException { if (!(msg instanceof TcpDiscoveryPingRequest)) { if (cntConnCheckMsg && msg instanceof TcpDiscoveryConnectionCheckMessage) connCheckStatusMsgCntSent++; super.writeToSocket(sock, out, msg, timeout); return; } if (timeout >= IgniteConfiguration.DFLT_FAILURE_DETECTION_TIMEOUT) { validTimeout = false; throw new IgniteCheckedException("Invalid timeout: " + timeout); } if (writeToSockTimeoutWait) { try { Thread.sleep(timeout); } catch (InterruptedException ignored) { // No-op. } } else super.writeToSocket(sock, out, msg, timeout); } /** {@inheritDoc} */ protected void writeToSocket(TcpDiscoveryAbstractMessage msg, Socket sock, int res, long timeout) throws IOException { if (cntConnCheckMsg && msg instanceof TcpDiscoveryConnectionCheckMessage) connCheckStatusMsgCntReceived++; super.writeToSocket(msg, sock, res, timeout); } /** * */ private void resetState() { openSockTimeout = false; openSockTimeoutWait = false; writeToSockTimeoutWait = false; err = null; validTimeout = true; connCheckStatusMsgCntSent = 0; connCheckStatusMsgCntReceived = 0; cntConnCheckMsg = false; } } }