org.apache.hadoop.raid.TestDirectoryRaidDfs.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.raid.TestDirectoryRaidDfs.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.raid;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.zip.CRC32;

import junit.framework.TestCase;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.DistributedFileSystem;
import org.apache.hadoop.hdfs.DistributedRaidFileSystem;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.RaidDFSUtil;
import org.apache.hadoop.hdfs.TestRaidDfs;
import org.apache.hadoop.hdfs.protocol.LocatedBlock;
import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
import org.apache.hadoop.raid.DirectoryStripeReader.BlockInfo;

public class TestDirectoryRaidDfs extends TestCase {
    final static String TEST_DIR = new File(System.getProperty("test.build.data", "build/contrib/raid/test/data"))
            .getAbsolutePath();
    final static long RELOAD_INTERVAL = 1000;
    final static Log LOG = LogFactory.getLog("org.apache.hadoop.raid.TestDirectoryRaidDecoder");
    final static int NUM_DATANODES = 3;
    static {
        ParityFilePair.disableCacheUsedInTestOnly();
    }
    final static long blockSize = 8192L;
    final static long halfBlock = blockSize / 2;
    final static long[][] fileSizes = {
            //Small file directory
            { 2000L, 3000L, 2000L }, { 3000L, 3000L, 3000L }, { 511L, 3584L, 3000L, 1234L, 512L, 1234L, 3000L },
            //Identical Block Size File Directory
            { 1000L, blockSize, 2 * blockSize, 4000L }, { blockSize, blockSize, 4 * blockSize + 1 },
            { blockSize + halfBlock, 2 * blockSize + halfBlock, halfBlock },
            { blockSize + 1, 7 * blockSize + 1, 2 * blockSize + 1, 3 * blockSize + 1 },
            //Different Block Size File Directory
            { 1000, blockSize, 2 * blockSize, 2 * blockSize + 1 },
            { blockSize, 2 * blockSize, 3 * blockSize, 4 * blockSize },
            { blockSize + 1, 9 * blockSize + 1, 2 * blockSize + 1, blockSize + 1 } };
    final static long[][] blockSizes = {
            // Small file directory
            { blockSize, blockSize, blockSize }, { blockSize, blockSize, blockSize },
            { blockSize, blockSize, blockSize, blockSize, blockSize, blockSize, blockSize },
            //Identical Block Size File Directory
            { blockSize, blockSize, blockSize, blockSize }, { blockSize, blockSize, blockSize },
            { blockSize, blockSize, blockSize }, { blockSize, blockSize, blockSize, blockSize },
            //Different Block Size File Directory
            { blockSize, blockSize, 2 * blockSize, blockSize },
            { blockSize, 2 * blockSize, 3 * blockSize, blockSize },
            { blockSize, 2 * blockSize, 3 * blockSize, blockSize } };

    Configuration conf;
    String namenode = null;
    String hftp = null;
    MiniDFSCluster dfs = null;
    FileSystem fileSys = null;
    String jobTrackerName = null;
    Codec codec;
    int stripeLength;

    public static void setupStripeStore(Configuration conf, FileSystem fs) {
        final String STRIPE_STORE_DIR = new File(TEST_DIR, "stripe_store." + System.currentTimeMillis())
                .getAbsolutePath();
        conf.set(RaidNode.RAID_STRIPE_STORE_CLASS_KEY, "org.apache.hadoop.raid.LocalStripeStore");
        fs.getConf().set(RaidNode.RAID_STRIPE_STORE_CLASS_KEY, "org.apache.hadoop.raid.LocalStripeStore");
        conf.set(LocalStripeStore.LOCAL_STRIPE_STORE_DIR_KEY + "." + fs.getUri().getAuthority(), STRIPE_STORE_DIR);
        fs.getConf().set(LocalStripeStore.LOCAL_STRIPE_STORE_DIR_KEY + "." + fs.getUri().getAuthority(),
                STRIPE_STORE_DIR);
        RaidNode.createStripeStore(conf, true, null);
    }

    private void mySetup(String erasureCode, int rsParityLength) throws Exception {
        new File(TEST_DIR).mkdirs(); // Make sure data directory exists
        conf = new Configuration();
        conf.setInt("raid.encoder.bufsize", 128);
        conf.setInt("raid.decoder.bufsize", 128);

        Utils.loadTestCodecs(conf, stripeLength, stripeLength, 1, rsParityLength, "/destraid", "/destraidrs", false,
                true);
        codec = Codec.getCodec(erasureCode);

        // scan all policies once every 5 second
        conf.setLong("raid.policy.rescan.interval", 5000);

        // Reduce run time for the test.
        conf.setInt("dfs.client.max.block.acquire.failures", 1);
        conf.setInt("dfs.client.baseTimeWindow.waitOn.BlockMissingException", 10);

        // do not use map-reduce cluster for Raiding
        conf.set("raid.classname", "org.apache.hadoop.raid.LocalRaidNode");

        conf.set("raid.server.address", "localhost:0");
        // Avoid datanode putting blocks under subdir directory. Corruptblock function
        // can only corrupt blocks under the current directory
        conf.setInt("dfs.datanode.numblocks", 1000);

        dfs = new MiniDFSCluster(conf, NUM_DATANODES, true, null);
        dfs.waitActive();
        fileSys = dfs.getFileSystem();
        setupStripeStore(conf, fileSys);
        namenode = fileSys.getUri().toString();
        hftp = "hftp://localhost.localdomain:" + dfs.getNameNodePort();

        FileSystem.setDefaultUri(conf, namenode);
        //Don't allow empty file to be raid
        conf.setLong(RaidNode.MINIMUM_RAIDABLE_FILESIZE_KEY, 1L);
    }

    private void myTearDown() throws Exception {
        if (dfs != null) {
            dfs.shutdown();
        }
    }

    static private DistributedRaidFileSystem getRaidFS(FileSystem fileSys, Configuration conf) throws IOException {
        DistributedFileSystem dfs = (DistributedFileSystem) fileSys;
        Configuration clientConf = new Configuration(conf);
        clientConf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.DistributedRaidFileSystem");
        clientConf.set("fs.raid.underlyingfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem");
        clientConf.setBoolean("fs.hdfs.impl.disable.cache", true);
        URI dfsUri = dfs.getUri();
        return (DistributedRaidFileSystem) FileSystem.get(dfsUri, clientConf);
    }

    static public void corruptBlocksInDirectory(Configuration conf, Path srcDir, long[] crcs,
            Integer[] listBlockNumToCorrupt, FileSystem fileSys, MiniDFSCluster cluster, boolean validate,
            boolean reportBadBlocks) throws IOException {
        long[] lengths = new long[crcs.length];
        // Get all block Info;
        ArrayList<BlockInfo> blocks = new ArrayList<BlockInfo>();
        List<FileStatus> lfs = RaidNode.listDirectoryRaidFileStatus(conf, fileSys, srcDir);
        assertNotNull(lfs);
        for (int fid = 0; fid < lfs.size(); fid++) {
            FileStatus fsStat = lfs.get(fid);
            long numBlock = RaidNode.getNumBlocks(fsStat);
            for (int bid = 0; bid < numBlock; bid++) {
                blocks.add(new BlockInfo(fid, bid));
            }
            lengths[fid] = fsStat.getLen();
        }
        HashSet<Integer> affectedFiles = new HashSet<Integer>();
        HashSet<Integer> affectedBlocks = new HashSet<Integer>();
        // corrupt blocks
        for (int blockNumToCorrupt : listBlockNumToCorrupt) {
            if (blockNumToCorrupt >= blocks.size()) {
                continue;
            }
            BlockInfo bi = null;
            int blockIndex = blockNumToCorrupt;
            if (blockNumToCorrupt < 0) {
                blockIndex = blocks.size() + blockNumToCorrupt;
                if (blockIndex < 0) {
                    continue;
                }
            }
            if (affectedBlocks.contains(blockIndex)) {
                continue;
            }
            affectedBlocks.add(blockIndex);
            bi = blocks.get(blockIndex);
            FileStatus srcFileFs = lfs.get(bi.fileIdx);
            Path srcFile = srcFileFs.getPath();
            LOG.info("Corrupt block " + bi.blockId + " of file " + srcFile);
            LocatedBlocks locations = RaidDFSUtil.getBlockLocations((DistributedFileSystem) fileSys,
                    srcFile.toUri().getPath(), 0L, srcFileFs.getLen());
            TestRaidDfs.corruptBlock(srcFile, locations.get(bi.blockId).getBlock(), NUM_DATANODES, true, cluster);
            if (reportBadBlocks) {
                cluster.getNameNode().reportBadBlocks(new LocatedBlock[] { locations.get(bi.blockId) });
            }
            affectedFiles.add(bi.fileIdx);
        }
        // validate files
        if (validate) {
            DistributedRaidFileSystem raidfs = getRaidFS(fileSys, conf);
            for (Integer fid : affectedFiles) {
                FileStatus stat = lfs.get(fid);
                assertTrue(TestRaidDfs.validateFile(raidfs, stat.getPath(), lengths[fid], crcs[fid]));
                // test readFully
                byte[] filebytes = new byte[(int) stat.getLen()];
                FSDataInputStream stm = raidfs.open(stat.getPath());
                stm.readFully(0, filebytes);
                CRC32 crc = new CRC32();
                crc.update(filebytes, 0, filebytes.length);
                assertEquals(crcs[fid], crc.getValue());
            }
        }
    }

    /**
     * Create a bunch of files, corrupt blocks in some of them and ensure that
     * corrupted files can be read through DistributedRaidFileSystem
     */
    public void testRaidDirDfs(String code, Integer[][] corrupt) throws Exception {
        LOG.info("Test testRaidDirDfs for " + code + " started.");
        stripeLength = 3;
        try {
            mySetup(code, 2);
            for (int i = 0; i < corrupt.length; i++) {
                Path srcDir = new Path("/user/dhruba/dir" + i);
                assert fileSizes.length == blockSizes.length;
                Codec curCodec = codec;
                for (int j = 0; j < fileSizes.length; j++) {
                    LOG.info(" Test " + code + " at corrupt scenario " + i + " files scenario " + j);
                    assert fileSizes[j].length == blockSizes[j].length;
                    long[] crcs = new long[fileSizes[j].length];
                    int[] seeds = new int[fileSizes[j].length];
                    Path parityDir = new Path(codec.parityDirectory);
                    RaidDFSUtil.cleanUp(fileSys, srcDir);
                    RaidDFSUtil.cleanUp(fileSys, parityDir);
                    TestRaidDfs.createTestFiles(srcDir, fileSizes[j], blockSizes[j], crcs, seeds, fileSys,
                            (short) 1);
                    assertTrue(
                            RaidNode.doRaid(conf, fileSys.getFileStatus(srcDir), new Path(curCodec.parityDirectory),
                                    curCodec, new RaidNode.Statistics(), RaidUtils.NULL_PROGRESSABLE, false, 1, 1));
                    corruptBlocksInDirectory(conf, srcDir, crcs, corrupt[i], fileSys, dfs, true, false);
                    RaidDFSUtil.cleanUp(fileSys, srcDir);
                    RaidDFSUtil.cleanUp(fileSys, new Path(curCodec.parityDirectory));
                    RaidDFSUtil.cleanUp(fileSys, new Path("/tmp"));
                }
            }
        } catch (Exception e) {
            LOG.info("testRaidDirDfs Exception " + e.getMessage(), e);
            throw e;
        } finally {
            myTearDown();
        }
        LOG.info("Test testRaidDirDfs for " + code + " completed.");
    }

    public void testXORRaidDirDfs() throws Exception {
        testRaidDirDfs("xor", new Integer[][] { { 0 }, { 1 }, { 2 }, { -3 }, { -2 }, { -1 } });
    }

    public void testTooManyErrorsDecode() throws Exception {
        LOG.info("testTooManyErrorsDecode start");
        long blockSize = 8192L;
        stripeLength = 3;
        mySetup("xor", 1);
        long[][] fsizes = { { 2000L, 3000L, 2000L }, { blockSize + 1, blockSize + 1 },
                { 2 * blockSize, blockSize + blockSize / 2 } };
        long[][] bsizes = { { blockSize, blockSize, blockSize }, { blockSize, blockSize },
                { 2 * blockSize, blockSize } };
        Integer[][] corrupts = { { 0, 1 }, { 0, 2 }, { 1, 2 } };
        try {
            for (String code : RaidDFSUtil.codes) {
                Codec curCodec = Codec.getCodec(code);
                Path srcDir = new Path("/user/dhruba/" + code);
                for (int i = 0; i < corrupts.length; i++) {
                    for (int j = 0; j < fsizes.length; j++) {
                        long[] crcs = new long[fsizes[j].length];
                        int[] seeds = new int[fsizes[j].length];
                        Path parityDir = new Path(codec.parityDirectory);
                        RaidDFSUtil.cleanUp(fileSys, srcDir);
                        RaidDFSUtil.cleanUp(fileSys, parityDir);
                        TestRaidDfs.createTestFiles(srcDir, fsizes[j], bsizes[j], crcs, seeds, fileSys, (short) 1);
                        assertTrue(RaidNode.doRaid(conf, fileSys.getFileStatus(srcDir),
                                new Path(curCodec.parityDirectory), curCodec, new RaidNode.Statistics(),
                                RaidUtils.NULL_PROGRESSABLE, false, 1, 1));
                        boolean expectedExceptionThrown = false;
                        try {
                            corruptBlocksInDirectory(conf, srcDir, crcs, corrupts[i], fileSys, dfs, true, false);
                            // Should not reach.
                        } catch (IOException e) {
                            LOG.info("Expected exception caught" + e);
                            expectedExceptionThrown = true;
                        }
                        assertTrue(expectedExceptionThrown);
                    }
                }
            }
            LOG.info("testTooManyErrorsDecode complete");
        } finally {
            myTearDown();
        }
    }

    public void testTooManyErrorsEncode() throws Exception {
        LOG.info("testTooManyErrorsEncode complete");
        stripeLength = 3;
        mySetup("xor", 1);
        // Encoding should fail when even one block is corrupt.
        Random rand = new Random();
        try {
            for (String code : RaidDFSUtil.codes) {
                Codec curCodec = Codec.getCodec(code);
                Path srcDir = new Path("/user/dhruba/" + code);
                for (int j = 0; j < fileSizes.length; j++) {
                    long[] crcs = new long[fileSizes[j].length];
                    int[] seeds = new int[fileSizes[j].length];
                    Path parityDir = new Path(codec.parityDirectory);
                    RaidDFSUtil.cleanUp(fileSys, srcDir);
                    RaidDFSUtil.cleanUp(fileSys, parityDir);
                    TestRaidDfs.createTestFiles(srcDir, fileSizes[j], blockSizes[j], crcs, seeds, fileSys,
                            (short) 1);
                    corruptBlocksInDirectory(conf, srcDir, crcs, new Integer[] { rand.nextInt() % 3 }, fileSys, dfs,
                            false, false);
                    boolean expectedExceptionThrown = false;
                    try {
                        RaidNode.doRaid(conf, fileSys.getFileStatus(srcDir), new Path(curCodec.parityDirectory),
                                curCodec, new RaidNode.Statistics(), RaidUtils.NULL_PROGRESSABLE, false, 1, 1);
                        // Should not reach.
                    } catch (IOException e) {
                        LOG.info("Expected exception caught" + e);
                        expectedExceptionThrown = true;
                    }
                    assertTrue(expectedExceptionThrown);
                }
            }
            LOG.info("testTooManyErrorsEncode complete");
        } finally {
            myTearDown();
        }
    }
}