/** * 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.namenode; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hdfs.DFSClient; import org.apache.hadoop.hdfs.DFSTestUtil; import org.apache.hadoop.hdfs.MiniDFSCluster; import org.apache.hadoop.hdfs.protocol.ClientProtocol; import org.apache.hadoop.hdfs.protocol.DatanodeID; import org.apache.hadoop.hdfs.protocol.DatanodeInfo; import org.apache.hadoop.hdfs.protocol.FSConstants.DatanodeReportType; import org.apache.hadoop.hdfs.protocol.LocatedBlock; import org.apache.hadoop.hdfs.protocol.LocatedBlocks; import org.apache.hadoop.hdfs.server.datanode.DataNode; import org.apache.hadoop.hdfs.server.namenode.DatanodeDescriptor; import org.apache.hadoop.hdfs.server.namenode.FSNamesystem; import org.apache.hadoop.hdfs.server.namenode.NameNode; import org.apache.hadoop.security.UnixUserGroupInformation; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.*; public class TestDeprioritizeSlowDatanodes { private static MiniDFSCluster cluster; private static Configuration conf; private static final int BLOCK_SIZE = 1024; private static final int BLOCKS = 10; private static final int FILE_SIZE = BLOCKS * BLOCK_SIZE; @BeforeClass public static void setUpBeforeClass() throws Exception { conf = new Configuration(); conf.setInt("dfs.block.size", BLOCK_SIZE); conf.setLong("dfs.heartbeat.timeout.millis", 10000); // 10 sec System.setProperty("test.build.data", "build/test/data1"); cluster = new MiniDFSCluster(conf, 6, true, new String[] { "/r1", "/r1", "/r1", "/r2", "/r2", "/r2" }, new String[] { "h1", "h2", "h3", "h4", "h5", "h6" }); cluster.waitActive(true); updateDatanodeMap(cluster); } @AfterClass public static void tearDownAfterClass() throws Exception { cluster.shutdown(); } private static DatanodeID createDataNodeID(DataNode dn) { String ipAddr = dn.getSelfAddr().getAddress().getHostAddress(); int port = dn.getPort(); String storageID = dn.getStorageID(); DatanodeID dnId = new DatanodeID(ipAddr + ":" + port); dnId.setStorageID(storageID); return dnId; } /** * Does a lot of hacks to change namenode and datanode datastructures to * identify datanodes by the machine name rather than the IP address. This is * done since we can give each datanode a different hostname in a unit test * but not a different ip address. * * @param cluster * the {@link MiniDFSCluster} to operate on * @throws Exception */ private static void updateDatanodeMap(MiniDFSCluster cluster) throws Exception { FSNamesystem namesystem = cluster.getNameNode().namesystem; for (DataNode node : cluster.getDataNodes()) { // Get old descriptor. DatanodeID dnId = createDataNodeID(node); DatanodeDescriptor dnDs = namesystem.getDatanode(dnId); // Create new id and descriptor. DatanodeID newId = new DatanodeID(node.getMachineName(), dnDs.getStorageID(), dnDs.getInfoPort(), dnDs.getIpcPort()); DatanodeDescriptor newDS = new DatanodeDescriptor(newId, dnDs.getNetworkLocation(), dnDs.getHostName(), dnDs.getCapacity(), dnDs.getDfsUsed(), dnDs.getRemaining(), dnDs.getNamespaceUsed(), dnDs.getXceiverCount()); newDS.isAlive = true; // Overwrite NN maps with new descriptor. namesystem.writeLock(); namesystem.clusterMap.remove(dnDs); namesystem.resolveNetworkLocation(newDS); namesystem.unprotectedAddDatanode(newDS); namesystem.clusterMap.add(newDS); namesystem.writeUnlock(); // Overwrite DN map with new registration. node.setRegistrationName(node.getMachineName()); } } @Test public void testDepriotizeSlowDatanodes() throws Exception { // Create source file. String fileName = "/testDepriotizeSlowDatanodes"; DFSTestUtil.createFile(cluster.getFileSystem(), new Path(fileName), (long) FILE_SIZE, (short) 3, (long) 0); // Create RPC connections ClientProtocol srcNamenode = DFSClient.createRPCNamenode( NameNode.getAddress(cluster.getFileSystem().getUri().getAuthority()), conf, UnixUserGroupInformation.login(conf, true), 0).getProxy(); // Create destination file. LocatedBlocks lbksBeforeKill = srcNamenode.getBlockLocations(fileName, 0, Long.MAX_VALUE); LocatedBlock blkBeforeKill = lbksBeforeKill.get(0); // choose one block DatanodeInfo[] locsBeforeKill = blkBeforeKill.getLocations(); DatanodeInfo toKill = locsBeforeKill[0]; // kill datanode Thread.sleep(10000); // 10 sec LocatedBlocks lbksAfterKill = srcNamenode.getBlockLocations(fileName, 0, Long.MAX_VALUE); LocatedBlock blkAfterKill = lbksAfterKill.get(0); // choose one block DatanodeInfo[] locsAfterKill = blkAfterKill.getLocations(); assert(locsAfterKill.length == locsBeforeKill.length); assert(locsAfterKill.length > 1); // assert the the killed node is now at the bottom assert(locsAfterKill[locsAfterKill.length - 1].equals(toKill)); fileName = "/testDepriotizeSlowDatanodes-2"; DFSTestUtil.createFile(cluster.getFileSystem(), new Path(fileName), (long) FILE_SIZE, (short) 3, (long) 0); LocatedBlocks lbks = srcNamenode.getBlockLocations(fileName, 0, Long.MAX_VALUE); for (LocatedBlock lbk : lbks.getLocatedBlocks()) { DatanodeInfo[] locs = lbk.getLocations(); for (DatanodeInfo dstloc : locs) { assert(!dstloc.equals(toKill)); } } // assert that toKill was still not decommissioned // A failed assertion does not signify a bug. It just // means that our test may not have tested the correct // behavior because the dataNode was considered dead by NN. DatanodeInfo[] deadDataNodes = srcNamenode.getDatanodeReport(DatanodeReportType.DEAD); assert(!Arrays.<DatanodeInfo>asList(deadDataNodes).contains(toKill)); } }