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

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.raid.TestDirectoryRaidEncoder.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.util.Random;
import java.util.zip.CRC32;

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.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.RaidDFSUtil;
import org.apache.hadoop.hdfs.TestRaidDfs;

import junit.framework.TestCase;

public class TestDirectoryRaidEncoder extends TestCase {
    final static String TEST_DIR = new File(System.getProperty("test.build.data", "build/contrib/raid/test/data"))
            .getAbsolutePath();
    final static Log LOG = LogFactory.getLog("org.apache.hadoop.raid.TestDirectoryRaidEncoder");
    final static int NUM_DATANODES = 3;
    static {
        ParityFilePair.disableCacheUsedInTestOnly();
    }

    Configuration conf;
    String namenode = null;
    MiniDFSCluster dfs = null;
    FileSystem fileSys = null;

    private void mySetup() 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);
        // 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");
        //Don't allow empty file to be raid
        conf.setLong(RaidNode.MINIMUM_RAIDABLE_FILESIZE_KEY, 1L);

        dfs = new MiniDFSCluster(conf, NUM_DATANODES, true, null);
        dfs.waitActive();
        fileSys = dfs.getFileSystem();
        namenode = fileSys.getUri().toString();
        TestDirectoryRaidDfs.setupStripeStore(conf, fileSys);

        FileSystem.setDefaultUri(conf, namenode);
    }

    private Codec loadTestCodecs(String erasureCode, int stripeLength, boolean isDirRaid) throws Exception {
        Utils.loadTestCodecs(conf, stripeLength, stripeLength, 1, 3, "/destraid", "/destraidrs", false, isDirRaid);
        return Codec.getCodec(erasureCode);
    }

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

    private boolean doRaid(Configuration conf, FileSystem fileSys, Path srcPath, Codec codec) throws IOException {
        return RaidNode.doRaid(conf, fileSys.getFileStatus(srcPath), new Path(codec.parityDirectory), codec,
                new RaidNode.Statistics(), RaidUtils.NULL_PROGRESSABLE, false, 1, 1);
    }

    public void testAbnormalDirectory() throws Exception {
        mySetup();
        Codec codec = loadTestCodecs("xor", 4, true);
        try {
            Path sourceDir = new Path("/user/raid");
            Path parityFile = new Path("/destraid/user/raid");
            assertTrue(fileSys.mkdirs(sourceDir));
            LOG.info("Test non-leaf directory");
            assertFalse("Couldn't raid non-leaf directory ", doRaid(conf, fileSys, sourceDir.getParent(), codec));
            assertFalse(fileSys.exists(parityFile.getParent()));

            LOG.info("Test empty directory");
            assertFalse("Couldn't raid empty directory ", doRaid(conf, fileSys, sourceDir, codec));
            assertFalse(fileSys.exists(parityFile));

            LOG.info("Test empty file in the directory");
            Path emptyFile = new Path(sourceDir, "emptyFile");
            TestRaidDfs.createTestFile(fileSys, emptyFile, 1, 0, 8192L);
            assertTrue(fileSys.exists(emptyFile));
            assertFalse("No raidable files in the directory", doRaid(conf, fileSys, sourceDir, codec));
            assertFalse(fileSys.exists(parityFile));

            LOG.info("Test not enough blocks in the directory");
            Path file1 = new Path(sourceDir, "file1");
            Path file2 = new Path(sourceDir, "file2");
            TestRaidDfs.createTestFile(fileSys, file1, 1, 1, 8192L);
            TestRaidDfs.createTestFile(fileSys, file2, 1, 1, 8192L);
            LOG.info("Created two files with two blocks in total");
            assertTrue(fileSys.exists(file1));
            assertTrue(fileSys.exists(file2));
            assertFalse("Not enough blocks in the directory", doRaid(conf, fileSys, sourceDir, codec));
            assertFalse(fileSys.exists(parityFile));

        } finally {
            myTearDown();
        }
    }

    private void validateSingleFile(String code, FileSystem fileSys, Path sourceDir, int stripeLength, int blockNum,
            boolean lastPartial) throws Exception {
        LOG.info(
                "Test file with " + blockNum + " blocks and " + (lastPartial ? "partial" : "full") + " last block");
        Codec codec = loadTestCodecs(code, stripeLength, true);
        Path parityDir = new Path(codec.parityDirectory);
        RaidDFSUtil.cleanUp(fileSys, sourceDir);
        RaidDFSUtil.cleanUp(fileSys, parityDir);
        fileSys.mkdirs(sourceDir);

        Path file1 = new Path(sourceDir, "file1");
        if (!lastPartial) {
            TestRaidDfs.createTestFile(fileSys, file1, 2, blockNum, 8192L);
        } else {
            TestRaidDfs.createTestFilePartialLastBlock(fileSys, file1, 2, blockNum, 8192L);
        }
        Path parityFile = RaidNode.getOriginalParityFile(parityDir, sourceDir);
        // Do directory level raid
        LOG.info("Create a directory-raid parity file " + parityFile);
        assertTrue("Cannot raid directory " + sourceDir, doRaid(conf, fileSys, sourceDir, codec));
        assertEquals("Modification time should be the same", fileSys.getFileStatus(sourceDir).getModificationTime(),
                fileSys.getFileStatus(parityFile).getModificationTime());
        assertEquals("Replica num of source file should be reduced to 1",
                fileSys.getFileStatus(file1).getReplication(), 1);
        assertEquals("Replica num of parity file should be reduced to 1",
                fileSys.getFileStatus(parityFile).getReplication(), 1);
        long dirCRC = RaidDFSUtil.getCRC(fileSys, parityFile);
        long dirLen = fileSys.getFileStatus(parityFile).getLen();
        // remove the parity dir
        RaidDFSUtil.cleanUp(fileSys, parityDir);
        codec = loadTestCodecs(code, stripeLength, false);
        Path parityFile1 = RaidNode.getOriginalParityFile(parityDir, file1);
        LOG.info("Create a file-raid parity file " + parityFile1);
        assertTrue("Cannot raid file " + file1, doRaid(conf, fileSys, file1, codec));
        assertTrue("Parity file doesn't match when the file has " + blockNum + " blocks ",
                TestRaidDfs.validateFile(fileSys, parityFile1, dirLen, dirCRC));
    }

    public void testOneFileDirectory() throws Exception {
        mySetup();
        int stripeLength = 4;
        try {
            for (String code : RaidDFSUtil.codes) {
                LOG.info("testOneFileDirectory: Test code " + code);
                Codec codec = loadTestCodecs(code, stripeLength, true);
                Path sourceDir = new Path("/user/raid", code);
                assertTrue(fileSys.mkdirs(sourceDir));
                Path twoBlockFile = new Path(sourceDir, "twoBlockFile");
                ;
                LOG.info("Test one file with 2 blocks");
                TestRaidDfs.createTestFile(fileSys, twoBlockFile, 2, 2, 8192L);
                assertTrue(fileSys.exists(twoBlockFile));
                assertFalse("Not enough blocks in the directory",
                        RaidNode.doRaid(conf, fileSys.getFileStatus(sourceDir), new Path(codec.parityDirectory),
                                codec, new RaidNode.Statistics(), RaidUtils.NULL_PROGRESSABLE, false, 1, 1));
                fileSys.delete(twoBlockFile, true);

                LOG.info("Test one file with blocks less than one stripe");
                validateSingleFile(code, fileSys, sourceDir, stripeLength, 3, false);
                validateSingleFile(code, fileSys, sourceDir, stripeLength, 3, true);
                LOG.info("Test one file with one stripe blocks");
                validateSingleFile(code, fileSys, sourceDir, stripeLength, stripeLength, false);
                validateSingleFile(code, fileSys, sourceDir, stripeLength, stripeLength, true);

                LOG.info("Test one file with more than one stripe blocks");
                validateSingleFile(code, fileSys, sourceDir, stripeLength, stripeLength + 2, false);
                validateSingleFile(code, fileSys, sourceDir, stripeLength, stripeLength + 2, true);
            }
        } finally {
            myTearDown();
        }
    }

    private void validateMultipleFiles(String code, FileSystem fileSys, Path sourceDir, int stripeLength,
            long[] fileSizes, long blockSize, long singleFileBlockSize) throws Exception {
        long[] blockSizes = new long[fileSizes.length];
        for (int i = 0; i < fileSizes.length; i++)
            blockSizes[i] = blockSize;
        validateMultipleFiles(code, fileSys, sourceDir, stripeLength, fileSizes, blockSizes, singleFileBlockSize);

    }

    //
    // creates a file by grouping multiple files together
    // Returns its crc.
    //
    private long createDirectoryFile(FileSystem fileSys, Path name, int repl, long[] fileSizes, long[] blockSizes,
            int[] seeds, long blockSize) throws IOException {
        CRC32 crc = new CRC32();
        assert fileSizes.length == blockSizes.length;
        assert fileSizes.length == seeds.length;
        FSDataOutputStream stm = fileSys.create(name, true, fileSys.getConf().getInt("io.file.buffer.size", 4096),
                (short) repl, blockSize);
        byte[] zeros = new byte[(int) (blockSize)];
        for (int j = 0; j < zeros.length; j++) {
            zeros[j] = 0;
        }
        // fill random data into file
        for (int i = 0; i < fileSizes.length; i++) {
            assert blockSizes[i] <= blockSize;
            byte[] b = new byte[(int) blockSizes[i]];
            long numBlocks = fileSizes[i] / blockSizes[i];
            Random rand = new Random(seeds[i]);
            for (int j = 0; j < numBlocks; j++) {
                rand.nextBytes(b);
                stm.write(b);
                crc.update(b);
                int zeroLen = (int) (blockSize - blockSizes[i]);
                stm.write(zeros, 0, zeroLen);
                crc.update(zeros, 0, zeroLen);
            }
            long lastBlock = fileSizes[i] - numBlocks * blockSizes[i];
            if (lastBlock > 0) {
                b = new byte[(int) lastBlock];
                rand.nextBytes(b);
                stm.write(b);
                crc.update(b);
                if (i + 1 < fileSizes.length) {
                    // Not last block of file, write zero
                    int zeroLen = (int) (blockSize - lastBlock);
                    stm.write(zeros, 0, zeroLen);
                    crc.update(zeros, 0, zeroLen);
                }
            }
        }
        stm.close();
        return crc.getValue();
    }

    private void printFileCRC(FileSystem fs, Path file, long bufferSize) throws IOException {
        byte[] buffer = new byte[(int) bufferSize];
        FSDataInputStream stm = fs.open(file);
        StringBuilder sb = new StringBuilder();
        sb.append("CRC for file: " + file + " size " + fs.getFileStatus(file).getLen() + "\n");
        while (stm.read(buffer) >= 0) {
            CRC32 crc = new CRC32();
            crc.update(buffer);
            sb.append(" " + crc.getValue());
        }
        sb.append("\n");
        System.out.println(sb.toString());
        stm.close();
    }

    private void validateMultipleFiles(String code, FileSystem fileSys, Path sourceDir, int stripeLength,
            long[] fileSizes, long[] blockSizes, long blockSize) throws Exception {
        assert fileSizes.length == blockSizes.length;
        Codec codec = loadTestCodecs(code, stripeLength, true);
        Path parityDir = new Path(codec.parityDirectory);
        RaidDFSUtil.cleanUp(fileSys, sourceDir);
        RaidDFSUtil.cleanUp(fileSys, parityDir);
        fileSys.mkdirs(sourceDir);

        LOG.info("Create files under directory " + sourceDir);
        Random rand = new Random();
        int[] seeds = new int[fileSizes.length];
        for (int i = 0; i < fileSizes.length; i++) {
            Path file = new Path(sourceDir, "file" + i);
            seeds[i] = rand.nextInt();
            TestRaidDfs.createTestFile(fileSys, file, 2, fileSizes[i], blockSizes[i], seeds[i]);
        }
        Path parityFile = RaidNode.getOriginalParityFile(parityDir, sourceDir);
        // Do directory level raid
        LOG.info("Create a directory-raid parity file " + parityFile);
        assertTrue("Cannot raid directory " + sourceDir, doRaid(conf, fileSys, sourceDir, codec));
        this.printFileCRC(fileSys, parityFile, blockSize);
        long dirCRC = RaidDFSUtil.getCRC(fileSys, parityFile);
        long dirLen = fileSys.getFileStatus(parityFile).getLen();

        assertEquals("Modification time should be the same", fileSys.getFileStatus(sourceDir).getModificationTime(),
                fileSys.getFileStatus(parityFile).getModificationTime());
        assertEquals("Replica num of parity file should be reduced to 1",
                fileSys.getFileStatus(parityFile).getReplication(), 1);
        for (int i = 0; i < fileSizes.length; i++) {
            Path file = new Path(sourceDir, "file" + i);
            assertEquals("Replica num of source file should be reduced to 1",
                    fileSys.getFileStatus(file).getReplication(), 1);
        }
        // remove the source dir and parity dir
        RaidDFSUtil.cleanUp(fileSys, sourceDir);
        RaidDFSUtil.cleanUp(fileSys, parityDir);
        fileSys.mkdirs(sourceDir);
        codec = loadTestCodecs(code, stripeLength, false);
        Path file1 = new Path(sourceDir, "file1");
        Path parityFile1 = RaidNode.getOriginalParityFile(parityDir, file1);
        LOG.info("Create a source file " + file1);
        this.createDirectoryFile(fileSys, file1, 1, fileSizes, blockSizes, seeds, blockSize);
        LOG.info("Create a file-raid parity file " + parityFile1);
        assertTrue("Cannot raid file " + file1, doRaid(conf, fileSys, file1, codec));
        this.printFileCRC(fileSys, parityFile1, blockSize);
        assertTrue("Parity file doesn't match", TestRaidDfs.validateFile(fileSys, parityFile1, dirLen, dirCRC));
    }

    public void testSmallFileDirectory() throws Exception {
        mySetup();
        int stripeLength = 4;
        long blockSize = 8192L;
        try {
            for (String code : RaidDFSUtil.codes) {
                LOG.info("testSmallFileDirectory: Test code " + code);
                Path sourceDir = new Path("/user/raid");
                validateMultipleFiles(code, fileSys, sourceDir, stripeLength, new long[] { 1000L, 4000L, 1000L },
                        blockSize, 4096L);
                validateMultipleFiles(code, fileSys, sourceDir, stripeLength,
                        new long[] { 2000L, 3000L, 2000L, 3000L }, blockSize, 3072L);
                validateMultipleFiles(code, fileSys, sourceDir, stripeLength,
                        new long[] { 3000L, 3000L, 3000L, 3000L }, blockSize, 3072L);
                validateMultipleFiles(code, fileSys, sourceDir, stripeLength,
                        new long[] { 511L, 3584L, 3000L, 1234L, 512L, 1234L, 3000L, 3234L, 511L }, blockSize,
                        3584L);
            }
        } finally {
            myTearDown();
        }
    }

    public void testIdenticalBlockSizeFileDirectory() throws Exception {
        mySetup();
        int stripeLength = 4;
        long blockSize = 8192L;
        try {
            for (String code : RaidDFSUtil.codes) {
                LOG.info("testIdenticalBlockSizeFileDirectory: Test code " + code);
                Path sourceDir = new Path("/user/raid");
                validateMultipleFiles(code, fileSys, sourceDir, stripeLength,
                        new long[] { 1000L, blockSize, 2 * blockSize, 4000L }, blockSize, blockSize);
                validateMultipleFiles(code, fileSys, sourceDir, stripeLength,
                        new long[] { blockSize, 2 * blockSize, 3 * blockSize, 4 * blockSize }, blockSize,
                        blockSize);
                int halfBlock = (int) blockSize / 2;
                validateMultipleFiles(
                        code, fileSys, sourceDir, stripeLength, new long[] { blockSize + halfBlock,
                                2 * blockSize + halfBlock, 3 * blockSize + halfBlock, 4 * blockSize + halfBlock },
                        blockSize, blockSize);
                validateMultipleFiles(code, fileSys, sourceDir, stripeLength,
                        new long[] { blockSize + 1, 9 * blockSize + 1, 2 * blockSize + 1, 3 * blockSize + 1 },
                        blockSize, blockSize);
            }
        } finally {
            myTearDown();
        }
    }

    public void testDifferentBlockSizeFileDirectory() throws Exception {
        mySetup();
        int stripeLength = 3;
        long blockSize = 8192L;
        try {
            for (String code : RaidDFSUtil.codes) {
                LOG.info("testDifferentBlockSizeFileDirectory: Test code " + code);
                Path sourceDir = new Path("/user/raid");
                validateMultipleFiles(code, fileSys, sourceDir, stripeLength,
                        new long[] { 1000, blockSize, 2 * blockSize, 2 * blockSize + 1 },
                        new long[] { blockSize, blockSize, 2 * blockSize, blockSize }, 2 * blockSize);
                validateMultipleFiles(code, fileSys, sourceDir, stripeLength,
                        new long[] { blockSize, 2 * blockSize, 3 * blockSize, 4 * blockSize },
                        new long[] { blockSize, 2 * blockSize, 3 * blockSize, blockSize }, 3 * blockSize);
                validateMultipleFiles(code, fileSys, sourceDir, stripeLength,
                        new long[] { blockSize + 1, 9 * blockSize + 1, 2 * blockSize + 1, blockSize + 1 },
                        new long[] { blockSize, 2 * blockSize, 3 * blockSize, blockSize }, 2 * blockSize + 512);
            }
        } finally {
            myTearDown();
        }
    }
}