org.apache.hadoop.hdfs.server.namenode.TestBlockPlacementPolicyRaid.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hdfs.server.namenode.TestBlockPlacementPolicyRaid.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.namenode;

import static org.junit.Assert.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import junit.framework.Assert;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.DFSTestUtil;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.protocol.Block;
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
import org.apache.hadoop.hdfs.protocol.LocatedBlock;
import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
import org.apache.hadoop.hdfs.server.namenode.BlockPlacementPolicyRaid.CachedFullPathNames;
import org.apache.hadoop.hdfs.server.namenode.BlockPlacementPolicyRaid.CachedLocatedBlocks;
import org.apache.hadoop.hdfs.util.InjectionEvent;
import org.apache.hadoop.net.Node;
import org.apache.hadoop.net.StaticMapping;
import org.apache.hadoop.raid.Codec;
import org.apache.hadoop.raid.Utils;
import org.apache.hadoop.util.InjectionEventI;
import org.apache.hadoop.util.InjectionHandler;
import org.junit.Test;

public class TestBlockPlacementPolicyRaid {
    TestBlockPlacementPolicyRaidInjectionHandler h;
    private Configuration conf = null;
    private MiniDFSCluster cluster = null;
    private FSNamesystem namesystem = null;
    private BlockPlacementPolicyRaid policy = null;
    private FileSystem fs = null;
    String[] rack1 = { "/rack1" };
    String[] rack2 = { "/rack2" };
    String[] host1 = { "host1.rack1.com" };
    String[] host2 = { "host2.rack2.com" };
    String xorPrefix = null;
    String raidTempPrefix = null;
    String raidrsTempPrefix = null;
    String raidrsHarTempPrefix = null;

    final static Log LOG = LogFactory.getLog(TestBlockPlacementPolicyRaid.class);

    protected void setupCluster() throws IOException {
        conf = new Configuration();
        conf.setLong("dfs.blockreport.intervalMsec", 1000L);
        conf.set("dfs.replication.pending.timeout.sec", "2");
        conf.setLong("dfs.block.size", 1L);
        conf.set("dfs.block.replicator.classname",
                "org.apache.hadoop.hdfs.server.namenode.BlockPlacementPolicyRaid");
        Utils.loadTestCodecs(conf, 2, 1, 3, "/raid", "/raidrs");
        conf.setInt("io.bytes.per.checksum", 1);
        // start the cluster with one datanode
        cluster = new MiniDFSCluster(conf, 1, true, rack1, host1);
        cluster.waitActive();
        namesystem = cluster.getNameNode().getNamesystem();
        Assert.assertTrue("BlockPlacementPolicy type is not correct.",
                namesystem.replicator instanceof BlockPlacementPolicyRaid);
        policy = (BlockPlacementPolicyRaid) namesystem.replicator;
        fs = cluster.getFileSystem();
        xorPrefix = Codec.getCodec("xor").parityDirectory;
        raidTempPrefix = Codec.getCodec("xor").tmpParityDirectory;
        raidrsTempPrefix = Codec.getCodec("rs").parityDirectory;
        raidrsHarTempPrefix = Codec.getCodec("rs").tmpParityDirectory;
    }

    @Test
    public void test3rdReplicaPlacement() throws Exception {
        conf = new Configuration();
        conf.set("dfs.block.replicator.classname",
                "org.apache.hadoop.hdfs.server.namenode.BlockPlacementPolicyRaid");
        // start the cluster with 3 datanodes and 3 racks
        String[] racks = new String[] { "/rack0", "/rack0", "/rack1", "/rack1", "/rack2", "/rack2" };
        String[] hosts = new String[] { "host0", "host1", "host2", "host3", "host4", "host5" };

        try {
            cluster = new MiniDFSCluster(conf, 6, true, racks, hosts);
            cluster.waitActive();

            namesystem = cluster.getNameNode().getNamesystem();
            Assert.assertTrue("BlockPlacementPolicy type is not correct.",
                    namesystem.replicator instanceof BlockPlacementPolicyRaid);
            policy = (BlockPlacementPolicyRaid) namesystem.replicator;
            fs = cluster.getFileSystem();

            final String filename = "/dir/file1";

            // an out-of-cluster client
            DatanodeDescriptor targets[] = policy.chooseTarget(filename, 3, null,
                    new ArrayList<DatanodeDescriptor>(), new ArrayList<Node>(), 100L);
            verifyNetworkLocations(targets, 2);

            // an in-cluster client
            targets = policy.chooseTarget(filename, 3, targets[0], new ArrayList<DatanodeDescriptor>(),
                    new ArrayList<Node>(), 100L);
            verifyNetworkLocations(targets, 3);
        } finally {
            if (cluster != null)
                cluster.shutdown();
        }
    }

    private void verifyNetworkLocations(DatanodeDescriptor[] locations, int expectedNumOfRacks) {
        HashSet<String> racks = new HashSet<String>();
        for (DatanodeDescriptor loc : locations) {
            racks.add(loc.getNetworkLocation());
        }
        assertEquals(expectedNumOfRacks, racks.size());
    }

    @Test
    public void testRefreshPolicy() throws Exception {
        final int THREAD_NUM = 10;
        conf = new Configuration();
        conf.set("dfs.block.replicator.classname",
                "org.apache.hadoop.hdfs.server.namenode.BlockPlacementPolicyDefault");
        conf.setInt("dfs.datanode.handler.count", 1);
        conf.setBoolean("fs.hdfs.impl.disable.cache", true);
        // start the cluster with one datanode
        cluster = new MiniDFSCluster(conf, 1, true, rack1, host1);
        cluster.waitActive();
        namesystem = cluster.getNameNode().getNamesystem();

        SimpleClient clients[] = new SimpleClient[THREAD_NUM];
        for (int i = 0; i < THREAD_NUM; i++) {
            clients[i] = new SimpleClient(conf, i);
        }
        for (int i = 0; i < THREAD_NUM; i++) {
            clients[i].start();
        }
        Thread.sleep(3000);
        try {
            namesystem.reconfigurePropertyImpl("dfs.block.replicator.classname",
                    "org.apache.hadoop.hdfs.server.namenode.BlockPlacementPolicyRaid");
            Assert.assertTrue("BlockPlacementPolicy type is not correct.",
                    namesystem.replicator instanceof BlockPlacementPolicyRaid);
            // set it back
            namesystem.reconfigurePropertyImpl("dfs.block.replicator.classname",
                    "org.apache.hadoop.hdfs.server.namenode.BlockPlacementPolicyDefault");
            Assert.assertTrue("BlockPlacementPolicy type is not correct.",
                    namesystem.replicator instanceof BlockPlacementPolicyDefault);
            Assert.assertFalse("BlockPlacementPolicy type is not correct.",
                    namesystem.replicator instanceof BlockPlacementPolicyRaid);
        } catch (Exception e) {
            LOG.error(e);
        } finally {
            for (int i = 0; i < THREAD_NUM; i++) {
                clients[i].shutdown();
            }
            if (cluster != null) {
                cluster.shutdown();
            }
        }
    }

    static class SimpleClient extends Thread {
        volatile boolean shouldRun;
        Configuration conf;
        FileSystem fs;
        Path dir;

        SimpleClient(Configuration conf, int threadId) throws Exception {
            shouldRun = true;
            Path p = new Path("/");
            fs = FileSystem.get(p.toUri(), conf);
            dir = new Path(p, Integer.toString(threadId));
        }

        public void shutdown() {
            shouldRun = false;
        }

        public void run() {
            while (shouldRun) {
                try {
                    fs.mkdirs(dir);
                } catch (Exception e) {
                    //ignore
                }
            }
        }
    }

    /**
     * Test BlockPlacementPolicyRaid.CachedLocatedBlocks and
     * BlockPlacementPolicyRaid.CachedFullPathNames
     * Verify that the results obtained from cache is the same as
     * the results obtained directly
     */
    @Test
    public void testCachedResults() throws IOException {
        setupCluster();
        try {
            refreshPolicy();
            // test blocks cache
            CachedLocatedBlocks cachedBlocks = new CachedLocatedBlocks(conf);
            String file1 = "/dir/file1";
            String file2 = "/dir/file2";
            DFSTestUtil.createFile(fs, new Path(file1), 3, (short) 1, 0L);
            DFSTestUtil.createFile(fs, new Path(file2), 4, (short) 1, 0L);

            FSInodeInfo inode1 = null;
            FSInodeInfo inode2 = null;
            namesystem.dir.readLock();
            try {
                inode1 = namesystem.dir.rootDir.getNode(file1);
                inode2 = namesystem.dir.rootDir.getNode(file2);
            } finally {
                namesystem.dir.readUnlock();
            }
            h = new TestBlockPlacementPolicyRaidInjectionHandler();
            InjectionHandler.set(h);

            verifyCachedBlocksResult(cachedBlocks, namesystem, file1, (INode) inode1, 0);
            verifyCachedBlocksResult(cachedBlocks, namesystem, file1, (INode) inode1, 1);
            verifyCachedBlocksResult(cachedBlocks, namesystem, file2, (INode) inode2, 1);
            verifyCachedBlocksResult(cachedBlocks, namesystem, file2, (INode) inode2, 2);
            try {
                Thread.sleep(1200L);
            } catch (InterruptedException e) {
            }
            verifyCachedBlocksResult(cachedBlocks, namesystem, file2, (INode) inode2, 3);
            verifyCachedBlocksResult(cachedBlocks, namesystem, file1, (INode) inode1, 4);

            // test full path cache
            CachedFullPathNames cachedFullPathNames = new CachedFullPathNames(conf);
            verifyCachedFullPathNameResult(cachedFullPathNames, inode1, 0);
            verifyCachedFullPathNameResult(cachedFullPathNames, inode1, 1);
            verifyCachedFullPathNameResult(cachedFullPathNames, inode2, 1);
            verifyCachedFullPathNameResult(cachedFullPathNames, inode2, 2);
            try {
                Thread.sleep(1200L);
            } catch (InterruptedException e) {
            }
            verifyCachedFullPathNameResult(cachedFullPathNames, inode2, 3);
            verifyCachedFullPathNameResult(cachedFullPathNames, inode1, 4);
        } finally {
            if (cluster != null) {
                cluster.shutdown();
            }
            InjectionHandler.clear();
        }
    }

    /**
     * Test the result of getCompanionBlocks() on the unraided files
     */
    @Test
    public void testGetCompanionBLocks() throws IOException {
        setupCluster();
        try {
            String file1 = "/dir/file1";
            String file2 = "/raid/dir/file2";
            String file3 = "/raidrs/dir/file3";
            // Set the policy to default policy to place the block in the default way
            setBlockPlacementPolicy(namesystem,
                    new BlockPlacementPolicyDefault(conf, namesystem, namesystem.clusterMap));
            DFSTestUtil.createFile(fs, new Path(file1), 3, (short) 1, 0L);
            DFSTestUtil.createFile(fs, new Path(file2), 4, (short) 1, 0L);
            DFSTestUtil.createFile(fs, new Path(file3), 8, (short) 1, 0L);
            Collection<LocatedBlock> companionBlocks;

            companionBlocks = getCompanionBlocks(namesystem, policy,
                    getBlocks(namesystem, file1).get(0).getBlock());
            Assert.assertTrue(companionBlocks == null || companionBlocks.size() == 0);

            companionBlocks = getCompanionBlocks(namesystem, policy,
                    getBlocks(namesystem, file1).get(2).getBlock());
            Assert.assertTrue(companionBlocks == null || companionBlocks.size() == 0);

            companionBlocks = getCompanionBlocks(namesystem, policy,
                    getBlocks(namesystem, file2).get(0).getBlock());
            Assert.assertEquals(1, companionBlocks.size());

            companionBlocks = getCompanionBlocks(namesystem, policy,
                    getBlocks(namesystem, file2).get(3).getBlock());
            Assert.assertEquals(1, companionBlocks.size());

            int rsParityLength = Codec.getCodec("rs").parityLength;
            companionBlocks = getCompanionBlocks(namesystem, policy,
                    getBlocks(namesystem, file3).get(0).getBlock());
            Assert.assertEquals(rsParityLength, companionBlocks.size());

            companionBlocks = getCompanionBlocks(namesystem, policy,
                    getBlocks(namesystem, file3).get(4).getBlock());
            Assert.assertEquals(rsParityLength, companionBlocks.size());

            companionBlocks = getCompanionBlocks(namesystem, policy,
                    getBlocks(namesystem, file3).get(6).getBlock());
            Assert.assertEquals(2, companionBlocks.size());
        } finally {
            if (cluster != null) {
                cluster.shutdown();
            }
        }
    }

    static void setBlockPlacementPolicy(FSNamesystem namesystem, BlockPlacementPolicy policy) {
        namesystem.writeLock();
        try {
            namesystem.replicator = policy;
        } finally {
            namesystem.writeUnlock();
        }
    }

    /**
     * Test BlockPlacementPolicyRaid actually deletes the correct replica.
     * Start 2 datanodes and create 1 source file and its parity file.
     * 1) Start host1, create the parity file with replication 1
     * 2) Start host2, create the source file with replication 2
     * 3) Set repliation of source file to 1
     * Verify that the policy should delete the block with more companion blocks.
     */
    @Test
    public void testDeleteReplica() throws IOException {
        setupCluster();
        try {
            // Set the policy to default policy to place the block in the default way
            setBlockPlacementPolicy(namesystem,
                    new BlockPlacementPolicyDefault(conf, namesystem, namesystem.clusterMap));
            DatanodeDescriptor datanode1 = namesystem.datanodeMap.values().iterator().next();
            String source = "/dir/file";
            String parity = xorPrefix + source;

            final Path parityPath = new Path(parity);
            DFSTestUtil.createFile(fs, parityPath, 3, (short) 1, 0L);
            DFSTestUtil.waitReplication(fs, parityPath, (short) 1);

            // start one more datanode
            cluster.startDataNodes(conf, 1, true, null, rack2, host2, null);
            DatanodeDescriptor datanode2 = null;
            for (DatanodeDescriptor d : namesystem.datanodeMap.values()) {
                if (!d.getName().equals(datanode1.getName())) {
                    datanode2 = d;
                }
            }
            Assert.assertTrue(datanode2 != null);
            cluster.waitActive();
            final Path sourcePath = new Path(source);
            DFSTestUtil.createFile(fs, sourcePath, 5, (short) 2, 0L);
            DFSTestUtil.waitReplication(fs, sourcePath, (short) 2);

            refreshPolicy();
            Assert.assertEquals(source, policy.getSourceFile(parity, xorPrefix).name);

            List<LocatedBlock> sourceBlocks = getBlocks(namesystem, source);
            List<LocatedBlock> parityBlocks = getBlocks(namesystem, parity);
            Assert.assertEquals(5, sourceBlocks.size());
            Assert.assertEquals(3, parityBlocks.size());

            // verify the result of getCompanionBlocks()
            Collection<LocatedBlock> companionBlocks;
            companionBlocks = getCompanionBlocks(namesystem, policy, sourceBlocks.get(0).getBlock());
            verifyCompanionBlocks(companionBlocks, sourceBlocks, parityBlocks, new int[] { 0, 1 }, new int[] { 0 });

            companionBlocks = getCompanionBlocks(namesystem, policy, sourceBlocks.get(1).getBlock());
            verifyCompanionBlocks(companionBlocks, sourceBlocks, parityBlocks, new int[] { 0, 1 }, new int[] { 0 });

            companionBlocks = getCompanionBlocks(namesystem, policy, sourceBlocks.get(2).getBlock());
            verifyCompanionBlocks(companionBlocks, sourceBlocks, parityBlocks, new int[] { 2, 3 }, new int[] { 1 });

            companionBlocks = getCompanionBlocks(namesystem, policy, sourceBlocks.get(3).getBlock());
            verifyCompanionBlocks(companionBlocks, sourceBlocks, parityBlocks, new int[] { 2, 3 }, new int[] { 1 });

            companionBlocks = getCompanionBlocks(namesystem, policy, sourceBlocks.get(4).getBlock());
            verifyCompanionBlocks(companionBlocks, sourceBlocks, parityBlocks, new int[] { 4 }, new int[] { 2 });

            companionBlocks = getCompanionBlocks(namesystem, policy, parityBlocks.get(0).getBlock());
            verifyCompanionBlocks(companionBlocks, sourceBlocks, parityBlocks, new int[] { 0, 1 }, new int[] { 0 });

            companionBlocks = getCompanionBlocks(namesystem, policy, parityBlocks.get(1).getBlock());
            verifyCompanionBlocks(companionBlocks, sourceBlocks, parityBlocks, new int[] { 2, 3 }, new int[] { 1 });

            companionBlocks = getCompanionBlocks(namesystem, policy, parityBlocks.get(2).getBlock());
            verifyCompanionBlocks(companionBlocks, sourceBlocks, parityBlocks, new int[] { 4 }, new int[] { 2 });

            // Set the policy back to raid policy. We have to create a new object
            // here to clear the block location cache
            refreshPolicy();
            setBlockPlacementPolicy(namesystem, policy);
            // verify policy deletes the correct blocks. companion blocks should be
            // evenly distributed.
            fs.setReplication(sourcePath, (short) 1);
            DFSTestUtil.waitReplication(fs, sourcePath, (short) 1);
            Map<String, Integer> counters = new HashMap<String, Integer>();
            refreshPolicy();
            for (int i = 0; i < parityBlocks.size(); i++) {
                companionBlocks = getCompanionBlocks(namesystem, policy, parityBlocks.get(i).getBlock());

                counters = BlockPlacementPolicyRaid.countCompanionBlocks(companionBlocks)[0];
                Assert.assertTrue(counters.get(datanode1.getName()) >= 1 && counters.get(datanode1.getName()) <= 2);
                Assert.assertTrue(counters.get(datanode1.getName())
                        + counters.get(datanode2.getName()) == companionBlocks.size());

                counters = BlockPlacementPolicyRaid.countCompanionBlocks(companionBlocks)[1];
                Assert.assertTrue(counters.get(datanode1.getParent().getName()) >= 1
                        && counters.get(datanode1.getParent().getName()) <= 2);
                Assert.assertTrue(counters.get(datanode1.getParent().getName())
                        + counters.get(datanode2.getParent().getName()) == companionBlocks.size());
            }
        } finally {
            if (cluster != null) {
                cluster.shutdown();
            }
        }
    }

    @Test
    public void testParentPath() {
        String path = null;

        path = "/foo/bar";
        Assert.assertEquals(new Path(path).getParent().toString(), BlockPlacementPolicyRaid.getParentPath(path));

        path = "/foo/bar/";
        Assert.assertEquals(new Path(path).getParent().toString(), BlockPlacementPolicyRaid.getParentPath(path));

        path = "/foo";
        Assert.assertEquals(new Path(path).getParent().toString(), BlockPlacementPolicyRaid.getParentPath(path));

        path = "/foo/";
        Assert.assertEquals(new Path(path).getParent().toString(), BlockPlacementPolicyRaid.getParentPath(path));
    }

    // create a new BlockPlacementPolicyRaid to clear the cache
    private void refreshPolicy() {
        policy = new BlockPlacementPolicyRaid();
        policy.initialize(conf, namesystem, namesystem.clusterMap, null, null, namesystem);
    }

    private void verifyCompanionBlocks(Collection<LocatedBlock> companionBlocks, List<LocatedBlock> sourceBlocks,
            List<LocatedBlock> parityBlocks, int[] sourceBlockIndexes, int[] parityBlockIndexes) {
        Set<Block> blockSet = new HashSet<Block>();
        for (LocatedBlock b : companionBlocks) {
            blockSet.add(b.getBlock());
        }
        Assert.assertEquals(sourceBlockIndexes.length + parityBlockIndexes.length, blockSet.size());
        for (int index : sourceBlockIndexes) {
            Assert.assertTrue(blockSet.contains(sourceBlocks.get(index).getBlock()));
        }
        for (int index : parityBlockIndexes) {
            Assert.assertTrue(blockSet.contains(parityBlocks.get(index).getBlock()));
        }
    }

    private void verifyCachedFullPathNameResult(CachedFullPathNames cachedFullPathNames, FSInodeInfo inode,
            int cachedReads) throws IOException {
        Assert.assertEquals(inode.getFullPathName(), policy.getFullPathName(inode));
        Assert.assertEquals(cachedReads,
                h.events.get(InjectionEvent.BLOCKPLACEMENTPOLICYRAID_CACHED_PATH).intValue());
    }

    private void verifyCachedBlocksResult(CachedLocatedBlocks cachedBlocks, FSNamesystem namesystem, String file,
            INode f, int cachedReads) throws IOException {
        long len = namesystem.getFileInfo(file).getLen();
        List<LocatedBlock> res1 = namesystem.getBlockLocations(file, 0L, len).getLocatedBlocks();
        List<LocatedBlock> res2 = policy.getLocatedBlocks(file, f);
        for (int i = 0; i < res1.size(); i++) {
            Assert.assertEquals(res1.get(i).getBlock(), res2.get(i).getBlock());
        }
        Assert.assertEquals(cachedReads,
                h.events.get(InjectionEvent.BLOCKPLACEMENTPOLICYRAID_CACHED_BLOCKS).intValue());
    }

    private Collection<LocatedBlock> getCompanionBlocks(FSNamesystem namesystem, BlockPlacementPolicyRaid policy,
            Block block) throws IOException {
        INodeFile inode = namesystem.blocksMap.getINode(block);
        BlockPlacementPolicyRaid.FileInfo info = policy.getFileInfo(inode, inode.getFullPathName());
        return policy.getCompanionBlocks(inode.getFullPathName(), info, block, inode);
    }

    private List<LocatedBlock> getBlocks(FSNamesystem namesystem, String file) throws IOException {
        FileStatus stat = namesystem.getFileInfo(file);
        return namesystem.getBlockLocations(file, 0, stat.getLen()).getLocatedBlocks();
    }

    class TestBlockPlacementPolicyRaidInjectionHandler extends InjectionHandler {
        Map<InjectionEventI, Integer> events = new HashMap<InjectionEventI, Integer>();

        public TestBlockPlacementPolicyRaidInjectionHandler() {
            events.put(InjectionEvent.BLOCKPLACEMENTPOLICYRAID_CACHED_BLOCKS, 0);
            events.put(InjectionEvent.BLOCKPLACEMENTPOLICYRAID_CACHED_PATH, 0);
        }

        @Override
        public void _processEvent(InjectionEventI event, Object... args) {
            if (event == InjectionEvent.BLOCKPLACEMENTPOLICYRAID_CACHED_BLOCKS
                    || event == InjectionEvent.BLOCKPLACEMENTPOLICYRAID_CACHED_PATH) {
                events.put(event, events.get(event) + 1);
            }
        }
    }
}