org.apache.hadoop.hdfs.server.datanode.TestDatanodeProtocolRetryPolicy.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hdfs.server.datanode.TestDatanodeProtocolRetryPolicy.java

Source

/**
 * 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.hadoop.hdfs.server.datanode;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.concurrent.ThreadLocalRandom;

import com.google.common.base.Supplier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.impl.Log4JLogger;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.ha.HAServiceProtocol.HAServiceState;
import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.DFSTestUtil;
import org.apache.hadoop.hdfs.HdfsConfiguration;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.protocolPB.DatanodeProtocolClientSideTranslatorPB;
import org.apache.hadoop.hdfs.server.protocol.BlockReportContext;
import org.apache.hadoop.hdfs.server.protocol.DatanodeCommand;
import org.apache.hadoop.hdfs.server.protocol.DatanodeRegistration;
import org.apache.hadoop.hdfs.server.protocol.HeartbeatResponse;
import org.apache.hadoop.hdfs.server.protocol.NamespaceInfo;
import org.apache.hadoop.hdfs.server.protocol.NNHAStatusHeartbeat;
import org.apache.hadoop.hdfs.server.protocol.RegisterCommand;
import org.apache.hadoop.hdfs.server.protocol.StorageBlockReport;
import org.apache.hadoop.hdfs.server.protocol.StorageReport;
import org.apache.hadoop.hdfs.server.protocol.VolumeFailureSummary;
import org.apache.hadoop.test.GenericTestUtils;
import org.apache.log4j.Level;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

/**
 * This tests DatanodeProtocol retry policy
 */
public class TestDatanodeProtocolRetryPolicy {
    private static final Log LOG = LogFactory.getLog(TestDatanodeProtocolRetryPolicy.class);
    private static final String DATA_DIR = MiniDFSCluster.getBaseDirectory() + "data";
    private DataNode dn;
    private Configuration conf;
    private boolean tearDownDone;
    ArrayList<StorageLocation> locations = new ArrayList<StorageLocation>();
    private final static String CLUSTER_ID = "testClusterID";
    private final static String POOL_ID = "BP-TEST";
    private final static InetSocketAddress NN_ADDR = new InetSocketAddress("localhost", 5020);
    private static DatanodeRegistration datanodeRegistration = DFSTestUtil.getLocalDatanodeRegistration();

    static {
        ((Log4JLogger) LOG).getLogger().setLevel(Level.ALL);
    }

    /**
     * Starts an instance of DataNode
     * @throws IOException
     */
    @Before
    public void startUp() throws IOException, URISyntaxException {
        tearDownDone = false;
        conf = new HdfsConfiguration();
        conf.set(DFSConfigKeys.DFS_DATANODE_DATA_DIR_KEY, DATA_DIR);
        conf.set(DFSConfigKeys.DFS_DATANODE_ADDRESS_KEY, "0.0.0.0:0");
        conf.set(DFSConfigKeys.DFS_DATANODE_HTTP_ADDRESS_KEY, "0.0.0.0:0");
        conf.set(DFSConfigKeys.DFS_DATANODE_IPC_ADDRESS_KEY, "0.0.0.0:0");
        conf.setInt(CommonConfigurationKeys.IPC_CLIENT_CONNECT_MAX_RETRIES_KEY, 0);
        FileSystem.setDefaultUri(conf, "hdfs://" + NN_ADDR.getHostName() + ":" + NN_ADDR.getPort());
        File dataDir = new File(DATA_DIR);
        FileUtil.fullyDelete(dataDir);
        dataDir.mkdirs();
        StorageLocation location = StorageLocation.parse(dataDir.getPath());
        locations.add(location);
    }

    /**
     * Cleans the resources and closes the instance of datanode
     * @throws IOException if an error occurred
     */
    @After
    public void tearDown() throws IOException {
        if (!tearDownDone && dn != null) {
            try {
                dn.shutdown();
            } catch (Exception e) {
                LOG.error("Cannot close: ", e);
            } finally {
                File dir = new File(DATA_DIR);
                if (dir.exists())
                    Assert.assertTrue("Cannot delete data-node dirs", FileUtil.fullyDelete(dir));
            }
            tearDownDone = true;
        }
    }

    private void waitForBlockReport(final DatanodeProtocolClientSideTranslatorPB mockNN) throws Exception {
        GenericTestUtils.waitFor(new Supplier<Boolean>() {
            @Override
            public Boolean get() {
                try {
                    Mockito.verify(mockNN).blockReport(Mockito.eq(datanodeRegistration), Mockito.eq(POOL_ID),
                            Mockito.<StorageBlockReport[]>anyObject(), Mockito.<BlockReportContext>anyObject());
                    return true;
                } catch (Throwable t) {
                    LOG.info("waiting on block report: " + t.getMessage());
                    return false;
                }
            }
        }, 500, 100000);
    }

    /**
     * Verify the following scenario.
     * 1. The initial DatanodeProtocol.registerDatanode succeeds.
     * 2. DN starts heartbeat process.
     * 3. In the first heartbeat, NN asks DN to reregister.
     * 4. DN calls DatanodeProtocol.registerDatanode.
     * 5. DatanodeProtocol.registerDatanode throws EOFException.
     * 6. DN retries.
     * 7. DatanodeProtocol.registerDatanode succeeds.
     */
    @Test(timeout = 60000)
    public void testDatanodeRegistrationRetry() throws Exception {
        final DatanodeProtocolClientSideTranslatorPB namenode = mock(DatanodeProtocolClientSideTranslatorPB.class);

        Mockito.doAnswer(new Answer<DatanodeRegistration>() {
            int i = 0;

            @Override
            public DatanodeRegistration answer(InvocationOnMock invocation) throws Throwable {
                i++;
                if (i > 1 && i < 5) {
                    LOG.info("mockito exception " + i);
                    throw new EOFException("TestDatanodeProtocolRetryPolicy");
                } else {
                    DatanodeRegistration dr = (DatanodeRegistration) invocation.getArguments()[0];
                    datanodeRegistration = new DatanodeRegistration(dr.getDatanodeUuid(), dr);
                    LOG.info("mockito succeeded " + datanodeRegistration);
                    return datanodeRegistration;
                }
            }
        }).when(namenode).registerDatanode(Mockito.any(DatanodeRegistration.class));

        when(namenode.versionRequest()).thenReturn(new NamespaceInfo(1, CLUSTER_ID, POOL_ID, 1L));

        Mockito.doAnswer(new Answer<HeartbeatResponse>() {
            int i = 0;

            @Override
            public HeartbeatResponse answer(InvocationOnMock invocation) throws Throwable {
                i++;
                HeartbeatResponse heartbeatResponse;
                if (i == 1) {
                    LOG.info("mockito heartbeatResponse registration " + i);
                    heartbeatResponse = new HeartbeatResponse(new DatanodeCommand[] { RegisterCommand.REGISTER },
                            new NNHAStatusHeartbeat(HAServiceState.ACTIVE, 1), null,
                            ThreadLocalRandom.current().nextLong() | 1L);
                } else {
                    LOG.info("mockito heartbeatResponse " + i);
                    heartbeatResponse = new HeartbeatResponse(new DatanodeCommand[0],
                            new NNHAStatusHeartbeat(HAServiceState.ACTIVE, 1), null,
                            ThreadLocalRandom.current().nextLong() | 1L);
                }
                return heartbeatResponse;
            }
        }).when(namenode).sendHeartbeat(Mockito.any(DatanodeRegistration.class), Mockito.any(StorageReport[].class),
                Mockito.anyLong(), Mockito.anyLong(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
                Mockito.any(VolumeFailureSummary.class), Mockito.anyBoolean());

        dn = new DataNode(conf, locations, null) {
            @Override
            DatanodeProtocolClientSideTranslatorPB connectToNN(InetSocketAddress nnAddr) throws IOException {
                Assert.assertEquals(NN_ADDR, nnAddr);
                return namenode;
            }
        };

        // Trigger a heartbeat so that it acknowledges the NN as active.
        dn.getAllBpOs().get(0).triggerHeartbeatForTests();

        waitForBlockReport(namenode);
    }
}