co.cask.cdap.hbase.wd.RowKeyDistributorTestBase.java Source code

Java tutorial

Introduction

Here is the source code for co.cask.cdap.hbase.wd.RowKeyDistributorTestBase.java

Source

/*
 * Copyright  2014 Cask Data, Inc.
 *
 * Licensed 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 co.cask.cdap.hbase.wd;

import co.cask.cdap.common.utils.Networks;
import co.cask.cdap.test.XSlowTests;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.output.NullOutputFormat;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import java.io.IOException;
import java.util.concurrent.Executors;

/**
 * Provides basic tests for row key distributor
 */
@Category(XSlowTests.class)
public abstract class RowKeyDistributorTestBase {

    // Controls for test suite for whether to run BeforeClass/AfterClass
    public static boolean runBefore = true;
    public static boolean runAfter = true;

    protected static final String TABLE_NAME = "table";
    protected static final byte[] TABLE = Bytes.toBytes(TABLE_NAME);
    protected static final byte[] CF = Bytes.toBytes("colfam");
    protected static final byte[] QUAL = Bytes.toBytes("qual");
    private final AbstractRowKeyDistributor keyDistributor;
    private static HBaseTestingUtility testingUtility;
    private static HTable hTable;

    public RowKeyDistributorTestBase(AbstractRowKeyDistributor keyDistributor) {
        this.keyDistributor = keyDistributor;
    }

    @BeforeClass
    public static void beforeClass() throws Exception {
        if (!runBefore) {
            return;
        }

        testingUtility = new HBaseTestingUtility();
        Configuration hConf = testingUtility.getConfiguration();
        hConf.set("yarn.is.minicluster", "true");
        // Tune down the connection thread pool size
        hConf.setInt("hbase.hconnection.threads.core", 5);
        hConf.setInt("hbase.hconnection.threads.max", 10);
        // Tunn down handler threads in regionserver
        hConf.setInt("hbase.regionserver.handler.count", 10);

        // Set to random port
        hConf.setInt("hbase.master.port", Networks.getRandomPort());
        hConf.setInt("hbase.master.info.port", Networks.getRandomPort());
        hConf.setInt("hbase.regionserver.port", Networks.getRandomPort());
        hConf.setInt("hbase.regionserver.info.port", Networks.getRandomPort());

        testingUtility.startMiniCluster();
        hTable = testingUtility.createTable(TABLE, CF);
    }

    @AfterClass
    public static void afterClass() throws Exception {
        if (!runAfter) {
            return;
        }
        testingUtility.shutdownMiniCluster();
    }

    @After
    public void after() throws Exception {
        testingUtility.truncateTable(hTable.getTableName());
    }

    /** Testing simple get. */
    @Test
    public void testGet() throws IOException, InterruptedException {
        // Testing simple get
        byte[] key = new byte[] { 123, 124, 122 };
        byte[] distributedKey = keyDistributor.getDistributedKey(key);
        byte[] value = Bytes.toBytes("some");

        hTable.put(new Put(distributedKey).add(CF, QUAL, value));

        Result result = hTable.get(new Get(distributedKey));
        Assert.assertArrayEquals(key, keyDistributor.getOriginalKey(result.getRow()));
        Assert.assertArrayEquals(value, result.getValue(CF, QUAL));
    }

    /** Test scan with start and stop key. */
    @Test
    public void testSimpleScanBounded() throws IOException {
        long origKeyPrefix = System.currentTimeMillis();

        int seekIntervalMinValue = 100;
        int seekIntervalMaxValue = 899;
        byte[] startKey = Bytes.toBytes(origKeyPrefix + seekIntervalMinValue);
        byte[] stopKey = Bytes.toBytes(origKeyPrefix + seekIntervalMaxValue + 1);
        Scan scan = new Scan(startKey, stopKey);
        testSimpleScanInternal(origKeyPrefix, scan, 500, 500, seekIntervalMinValue, seekIntervalMaxValue);
    }

    /** Test scan over the whole table. */
    @Test
    public void testSimpleScanUnbounded() throws IOException {
        long origKeyPrefix = System.currentTimeMillis();
        testSimpleScanInternal(origKeyPrefix, new Scan(), 500, 500, 0, 999);
    }

    /** Test scan without stop key. */
    @Test
    public void testSimpleScanWithoutStopKey() throws IOException {
        long origKeyPrefix = System.currentTimeMillis();
        int seekIntervalMinValue = 100;
        byte[] startKey = Bytes.toBytes(origKeyPrefix + seekIntervalMinValue);
        testSimpleScanInternal(origKeyPrefix, new Scan(startKey), 500, 500, 100, 999);
    }

    /** Test scan with start and stop key. */
    @Test
    public void testMapReduceBounded() throws IOException, InterruptedException, ClassNotFoundException {
        long origKeyPrefix = System.currentTimeMillis();

        int seekIntervalMinValue = 100;
        int seekIntervalMaxValue = 899;
        byte[] startKey = Bytes.toBytes(origKeyPrefix + seekIntervalMinValue);
        byte[] stopKey = Bytes.toBytes(origKeyPrefix + seekIntervalMaxValue + 1);
        Scan scan = new Scan(startKey, stopKey);
        testMapReduceInternal(origKeyPrefix, scan, 500, 500, seekIntervalMinValue, seekIntervalMaxValue);
    }

    /** Test scan over the whole table. */
    @Test
    public void testMapReduceUnbounded() throws IOException, InterruptedException, ClassNotFoundException {
        long origKeyPrefix = System.currentTimeMillis();
        testMapReduceInternal(origKeyPrefix, new Scan(), 500, 500, 0, 999);
    }

    private int writeTestData(long origKeyPrefix, int numRows, int rowKeySeed, int seekIntervalMinValue,
            int seekIntervalMaxValue) throws IOException {
        int valuesCountInSeekInterval = 0;
        for (int i = 0; i < numRows; i++) {
            int val = rowKeySeed + i - i * (i % 2) * 2; // i.e. 500, 499, 502, 497, 504, ...
            valuesCountInSeekInterval += (val >= seekIntervalMinValue && val <= seekIntervalMaxValue) ? 1 : 0;
            byte[] key = Bytes.toBytes(origKeyPrefix + val);
            byte[] distributedKey = keyDistributor.getDistributedKey(key);
            byte[] value = Bytes.toBytes(val);
            hTable.put(new Put(distributedKey).add(CF, QUAL, value));
        }
        return valuesCountInSeekInterval;
    }

    private void testSimpleScanInternal(long origKeyPrefix, Scan scan, int numValues, int startWithValue,
            int seekIntervalMinValue, int seekIntervalMaxValue) throws IOException {
        int valuesCountInSeekInterval = writeTestData(origKeyPrefix, numValues, startWithValue,
                seekIntervalMinValue, seekIntervalMaxValue);

        // TODO: add some filters to the scan for better testing
        ResultScanner distributedScanner = DistributedScanner.create(hTable, scan, keyDistributor,
                Executors.newFixedThreadPool(2));

        Result previous = null;
        int countMatched = 0;
        for (Result current : distributedScanner) {
            countMatched++;
            if (previous != null) {
                byte[] currentRowOrigKey = keyDistributor.getOriginalKey(current.getRow());
                byte[] previousRowOrigKey = keyDistributor.getOriginalKey(previous.getRow());
                Assert.assertTrue(Bytes.compareTo(currentRowOrigKey, previousRowOrigKey) >= 0);

                int currentValue = Bytes.toInt(current.getValue(CF, QUAL));
                Assert.assertTrue(currentValue >= seekIntervalMinValue);
                Assert.assertTrue(currentValue <= seekIntervalMaxValue);
            }
            previous = current;
        }

        Assert.assertEquals(valuesCountInSeekInterval, countMatched);
    }

    private void testMapReduceInternal(long origKeyPrefix, Scan scan, int numValues, int startWithValue,
            int seekIntervalMinValue, int seekIntervalMaxValue)
            throws IOException, InterruptedException, ClassNotFoundException {
        int valuesCountInSeekInterval = writeTestData(origKeyPrefix, numValues, startWithValue,
                seekIntervalMinValue, seekIntervalMaxValue);

        // Reading data
        Configuration conf = new Configuration(testingUtility.getConfiguration());
        conf.set("fs.defaultFS", "file:///");
        conf.set("fs.default.name", "file:///");
        conf.setInt("mapreduce.local.map.tasks.maximum", 16);
        conf.setInt("mapreduce.local.reduce.tasks.maximum", 16);
        Job job = Job.getInstance(conf, "testMapReduceInternal()-Job");
        TableMapReduceUtil.initTableMapperJob(TABLE_NAME, scan, RowCounterMapper.class,
                ImmutableBytesWritable.class, Result.class, job);

        // Substituting standard TableInputFormat which was set in TableMapReduceUtil.initTableMapperJob(...)
        job.setInputFormatClass(WdTableInputFormat.class);
        keyDistributor.addInfo(job.getConfiguration());

        job.setOutputFormatClass(NullOutputFormat.class);
        job.setNumReduceTasks(0);

        boolean succeeded = job.waitForCompletion(true);
        Assert.assertTrue(succeeded);

        long mapInputRecords = job.getCounters().findCounter(RowCounterMapper.Counters.ROWS).getValue();
        Assert.assertEquals(valuesCountInSeekInterval, mapInputRecords);

        // Need to kill the job after completion, after it could leave MRAppMaster running not terminated.
        // Not sure what causing this, but maybe problem in MiniYarnCluster
        job.killJob();
    }

    /**
     * Mapper that runs the count.
     * NOTE: it was copied from RowCounter class
     */
    static class RowCounterMapper extends TableMapper<ImmutableBytesWritable, Result> {
        /** Counter enumeration to count the actual rows. */
        public enum Counters {
            ROWS
        }

        @Override
        public void map(ImmutableBytesWritable row, Result values, Context context) throws IOException {
            for (Cell cell : values.rawCells()) {
                if (cell.getValueLength() > 0) {
                    context.getCounter(Counters.ROWS).increment(1);
                    break;
                }
            }
        }
    }
}