org.apache.hadoop.hdfs.TestLookasideCache.java Source code

Java tutorial

Introduction

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

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Random;
import java.util.zip.CRC32;

import org.apache.commons.logging.impl.Log4JLogger;
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.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;
import org.apache.hadoop.hdfs.metrics.LookasideMetrics.LocalMetrics;
import org.apache.log4j.Level;

/**
 * This class tests the lookaside cache.
 */
public class TestLookasideCache extends junit.framework.TestCase {
    final static Log LOG = LogFactory.getLog("org.apache.hadoop.hdfs.TestLookasideCache");
    final static String TEST_DIR = new File(System.getProperty("test.build.data", "build/lookasidecache/test/data"))
            .getAbsolutePath();
    final static int NUM_DATANODES = 3;

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

    public void testCache() throws IOException {
        Configuration conf = new Configuration();
        long maxSize = 5 * 1024;
        long evictPercent = 20;
        conf.setLong(LookasideCache.CACHESIZE, maxSize);
        conf.setLong(LookasideCache.CACHEEVICT_PERCENT, evictPercent);
        LookasideCache cache = new LookasideCache(conf);
        LocalMetrics metrics = LookasideCache.getLocalMetrics();
        metrics.reset();

        assertTrue(cache.getCacheMaxSize() == maxSize);
        assertTrue(cache.getCacheSize() == 0);
        assertTrue(cache.getCacheEvictPercent() == evictPercent);

        // insert five elements into the cache, each of size 1024
        cache.addCache(new Path("one"), new Path("one"), 1024);
        cache.addCache(new Path("two"), new Path("two"), 1024);
        cache.addCache(new Path("three"), new Path("three"), 1024);
        cache.addCache(new Path("four"), new Path("four"), 1024);
        cache.addCache(new Path("five"), new Path("five"), 1024);
        assertTrue(cache.getCacheSize() == 5 * 1024);
        assertTrue(metrics.numAdd == 5);
        assertTrue(metrics.numAddNew == 5);
        assertTrue(metrics.numAddExisting == 0);

        // the cache is now full. If we add one more element, the oldest
        // two should be evicted
        cache.addCache(new Path("six"), new Path("six"), 512);
        assertTrue("cachesize is " + cache.getCacheSize(), cache.getCacheSize() == 3 * 1024 + 512);

        // verify that first two are not there. and the rest is there
        assertTrue(cache.getCache(new Path("one")) == null);
        assertTrue(cache.getCache(new Path("two")) == null);
        assertTrue(cache.getCache(new Path("three")) != null);
        assertTrue(cache.getCache(new Path("four")) != null);
        assertTrue(cache.getCache(new Path("five")) != null);
        assertTrue(cache.getCache(new Path("six")) != null);
        assertTrue(metrics.numEvict == 2);

        // make three the most recently used
        assertTrue(cache.getCache(new Path("three")) != null);
        assertTrue(metrics.numGetAttempts == 7);
        assertTrue(metrics.numGetHits == 5);

        // now we insert seven.
        cache.addCache(new Path("seven"), new Path("seven"), 512);
        assertTrue(cache.getCacheSize() == 4 * 1024);

        assertTrue(cache.getCache(new Path("one")) == null);
        assertTrue(cache.getCache(new Path("two")) == null);
        assertTrue(cache.getCache(new Path("three")) != null);
        assertTrue(cache.getCache(new Path("four")) != null);
        assertTrue(cache.getCache(new Path("five")) != null);
        assertTrue(cache.getCache(new Path("six")) != null);
        assertTrue(cache.getCache(new Path("seven")) != null);
    }

    public void testCacheFileSystem() throws IOException {

        // configure a cached filessytem, cache size is 10 KB.
        mySetup(10 * 1024L);

        try {
            // create a 5K file using the LookasideCache. This write
            // should be cached in the cache.
            Path file = new Path("/hdfs/testRead");
            long crc = createTestFile(lfs, file, 1, 5, 1024L);
            FileStatus stat = lfs.getFileStatus(file);
            LOG.info("Created " + file + ", crc=" + crc + ", len=" + stat.getLen());
            assertTrue(lfs.lookasideCache.getCacheSize() == 5 * 1024);

            // Test that readFully via the Lookasidecache fetches correct data
            // from the cache.
            FSDataInputStream stm = lfs.open(file);
            byte[] filebytes = new byte[(int) stat.getLen()];
            stm.readFully(0, filebytes);
            assertEquals(crc, bufferCRC(filebytes));
            stm.close();

            // assert that there is one element of size 5K in the cache
            assertEquals(5 * 1024, lfs.lookasideCache.getCacheSize());

            // create a 6K file using the LookasideCache. This is an
            // overwrite of the earlier file, so the cache should reflect
            // the new size of the file.
            crc = createTestFile(lfs, file, 1, 6, 1024L);
            stat = lfs.getFileStatus(file);
            LOG.info("Created " + file + ", crc=" + crc + ", len=" + stat.getLen());

            // assert that there is one element of size 6K in the cache
            assertEquals(6 * 1024, lfs.lookasideCache.getCacheSize());

            // verify reading file2 from the cache
            stm = lfs.open(file);
            filebytes = new byte[(int) stat.getLen()];
            stm.readFully(0, filebytes);
            assertEquals(crc, bufferCRC(filebytes));
            stm.close();

            // add a 5 KB file to the cache. This should start eviction of
            // the earlier file.
            Path file2 = new Path("/hdfs/testRead2");
            crc = createTestFile(lfs, file2, 1, 5, 1024L);
            stat = lfs.getFileStatus(file2);
            LOG.info("Created " + file2 + ", crc=" + crc + ", len=" + stat.getLen());
            assertEquals(5 * 1024, lfs.lookasideCache.getCacheSize());

            // move file2 to file3
            Path file3 = new Path("/hdfs/testRead3");
            assertTrue(lfs.rename(file2, file3));

            // delete file3. This should clear out the cache.
            lfs.delete(file3, false);
            assertEquals(0, lfs.lookasideCache.getCacheSize());

        } finally {
            myTearDown();
        }
    }

    private MiniDFSCluster dfs;
    private FileSystem fileSys;
    private LookasideCacheFileSystem lfs;
    private String namenode;
    private String hftp;

    // setup a LookasideCachedFileSystem
    private void mySetup(long cacheSize) throws IOException {
        Configuration conf = new Configuration();

        // create a HDFS cluster
        dfs = new MiniDFSCluster(conf, NUM_DATANODES, true, null);
        dfs.waitActive();
        fileSys = dfs.getFileSystem();
        namenode = fileSys.getUri().toString();
        hftp = "hftp://localhost.localdomain:" + dfs.getNameNodePort();
        FileSystem.setDefaultUri(conf, namenode);

        // create a client-side layered filesystem.
        // The cache size is 10 KB.
        lfs = getCachedHdfs(fileSys, conf, cacheSize);
    }

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

    /**
     * Returns a cached filesystem layered on top of the HDFS cluster
     */
    private LookasideCacheFileSystem getCachedHdfs(FileSystem fileSys, Configuration conf, long cacheSize)
            throws IOException {
        DistributedFileSystem dfs = (DistributedFileSystem) fileSys;
        Configuration clientConf = new Configuration(conf);

        clientConf.setLong(LookasideCache.CACHESIZE, cacheSize);
        clientConf.set("fs.lookasidecache.dir", TEST_DIR);
        clientConf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.LookasideCacheFileSystem");
        clientConf.set("fs.lookasidecache.underlyingfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem");
        URI dfsUri = dfs.getUri();
        FileSystem.closeAll();
        FileSystem lfs = FileSystem.get(dfsUri, clientConf);
        assertTrue("lfs not an instance of LookasideCacheFileSystem", lfs instanceof LookasideCacheFileSystem);
        return (LookasideCacheFileSystem) lfs;
    }

    //
    // creates a file and populate it with random data. Returns its crc.
    //
    private static long createTestFile(FileSystem fileSys, Path name, int repl, int numBlocks, long blocksize)
            throws IOException {
        CRC32 crc = new CRC32();
        Random rand = new Random();
        FSDataOutputStream stm = fileSys.create(name, true, fileSys.getConf().getInt("io.file.buffer.size", 4096),
                (short) repl, blocksize);
        // fill random data into file
        final byte[] b = new byte[(int) blocksize];
        for (int i = 0; i < numBlocks; i++) {
            rand.nextBytes(b);
            stm.write(b);
            crc.update(b);
        }
        stm.close();
        return crc.getValue();
    }

    /**
     * returns the CRC32 of the buffer
     */
    private long bufferCRC(byte[] buf) {
        CRC32 crc = new CRC32();
        crc.update(buf, 0, buf.length);
        return crc.getValue();
    }
}