org.apache.hadoop.hbase.procedure2.TestProcedureExecutor.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hbase.procedure2.TestProcedureExecutor.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.hbase.procedure2;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseCommonTestingUtility;
import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility.NoopProcedure;
import org.apache.hadoop.hbase.procedure2.store.NoopProcedureStore;
import org.apache.hadoop.hbase.testclassification.SmallTests;
import org.apache.hadoop.hbase.testclassification.MasterTests;
import org.apache.hadoop.hbase.util.Threads;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import static org.junit.Assert.assertEquals;

@Category({ MasterTests.class, SmallTests.class })
public class TestProcedureExecutor {
    private static final Log LOG = LogFactory.getLog(TestProcedureExecutor.class);

    private TestProcEnv procEnv;
    private NoopProcedureStore procStore;
    private ProcedureExecutor<TestProcEnv> procExecutor;

    private HBaseCommonTestingUtility htu;

    @Before
    public void setUp() throws Exception {
        htu = new HBaseCommonTestingUtility();

        // NOTE: The executor will be created by each test
        procEnv = new TestProcEnv();
        procStore = new NoopProcedureStore();
        procStore.start(1);
    }

    @After
    public void tearDown() throws Exception {
        procExecutor.stop();
        procStore.stop(false);
        procExecutor.join();
    }

    private void createNewExecutor(final Configuration conf, final int numThreads) throws Exception {
        procExecutor = new ProcedureExecutor(conf, procEnv, procStore);
        procExecutor.start(numThreads, true);
    }

    @Test(timeout = 60000)
    public void testWorkerStuck() throws Exception {
        // replace the executor
        final Configuration conf = new Configuration(htu.getConfiguration());
        conf.setFloat("hbase.procedure.worker.add.stuck.percentage", 0.5f);
        conf.setInt("hbase.procedure.worker.monitor.interval.msec", 500);
        conf.setInt("hbase.procedure.worker.stuck.threshold.msec", 750);

        final int NUM_THREADS = 2;
        createNewExecutor(conf, NUM_THREADS);

        Semaphore latch1 = new Semaphore(2);
        latch1.acquire(2);
        BusyWaitProcedure busyProc1 = new BusyWaitProcedure(latch1);

        Semaphore latch2 = new Semaphore(2);
        latch2.acquire(2);
        BusyWaitProcedure busyProc2 = new BusyWaitProcedure(latch2);

        long busyProcId1 = procExecutor.submitProcedure(busyProc1);
        long busyProcId2 = procExecutor.submitProcedure(busyProc2);
        long otherProcId = procExecutor.submitProcedure(new NoopProcedure());

        // wait until a new worker is being created
        int threads1 = waitThreadCount(NUM_THREADS + 1);
        LOG.info("new threads got created: " + (threads1 - NUM_THREADS));
        assertEquals(NUM_THREADS + 1, threads1);

        ProcedureTestingUtility.waitProcedure(procExecutor, otherProcId);
        assertEquals(true, procExecutor.isFinished(otherProcId));
        ProcedureTestingUtility.assertProcNotFailed(procExecutor, otherProcId);

        assertEquals(true, procExecutor.isRunning());
        assertEquals(false, procExecutor.isFinished(busyProcId1));
        assertEquals(false, procExecutor.isFinished(busyProcId2));

        // terminate the busy procedures
        latch1.release();
        latch2.release();

        LOG.info("set keep alive and wait threads being removed");
        procExecutor.setKeepAliveTime(500L, TimeUnit.MILLISECONDS);
        int threads2 = waitThreadCount(NUM_THREADS);
        LOG.info("threads got removed: " + (threads1 - threads2));
        assertEquals(NUM_THREADS, threads2);

        // terminate the busy procedures
        latch1.release();
        latch2.release();

        // wait for all procs to complete
        ProcedureTestingUtility.waitProcedure(procExecutor, busyProcId1);
        ProcedureTestingUtility.waitProcedure(procExecutor, busyProcId2);
        ProcedureTestingUtility.assertProcNotFailed(procExecutor, busyProcId1);
        ProcedureTestingUtility.assertProcNotFailed(procExecutor, busyProcId2);
    }

    @Test
    public void testSubmitBatch() throws Exception {
        Procedure[] procs = new Procedure[5];
        for (int i = 0; i < procs.length; ++i) {
            procs[i] = new NoopProcedure<TestProcEnv>();
        }

        // submit procedures
        createNewExecutor(htu.getConfiguration(), 3);
        procExecutor.submitProcedures(procs);

        // wait for procs to be completed
        for (int i = 0; i < procs.length; ++i) {
            final long procId = procs[i].getProcId();
            ProcedureTestingUtility.waitProcedure(procExecutor, procId);
            ProcedureTestingUtility.assertProcNotFailed(procExecutor, procId);
        }
    }

    private int waitThreadCount(final int expectedThreads) {
        while (procExecutor.isRunning()) {
            if (procExecutor.getWorkerThreadCount() == expectedThreads) {
                break;
            }
            LOG.debug("waiting for thread count=" + expectedThreads + " current="
                    + procExecutor.getWorkerThreadCount());
            Threads.sleepWithoutInterrupt(250);
        }
        return procExecutor.getWorkerThreadCount();
    }

    public static class BusyWaitProcedure extends NoopProcedure<TestProcEnv> {
        private final Semaphore latch;

        public BusyWaitProcedure(final Semaphore latch) {
            this.latch = latch;
        }

        @Override
        protected Procedure[] execute(final TestProcEnv env) {
            try {
                LOG.info("worker started " + this);
                if (!latch.tryAcquire(1, 30, TimeUnit.SECONDS)) {
                    throw new Exception("waited too long");
                }

                LOG.info("worker step 2 " + this);
                if (!latch.tryAcquire(1, 30, TimeUnit.SECONDS)) {
                    throw new Exception("waited too long");
                }
            } catch (Exception e) {
                LOG.error("got unexpected exception", e);
                setFailure("BusyWaitProcedure", e);
            }
            return null;
        }
    }

    private class TestProcEnv {
    }
}