/*
* 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.coprocessor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Coprocessor;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.MiniHBaseCluster;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.MetaTableAccessor;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.Mutation;
import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.hadoop.hbase.regionserver.HRegionServer;
import org.apache.hadoop.hbase.regionserver.RegionMergeTransaction;
import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
import org.apache.hadoop.hbase.testclassification.CoprocessorTests;
import org.apache.hadoop.hbase.testclassification.MediumTests;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Test;
import org.junit.experimental.categories.Category;
/**
* Tests invocation of the {@link org.apache.hadoop.hbase.coprocessor.RegionServerObserver}
* interface hooks at all appropriate times during normal HMaster operations.
*/
@Category({CoprocessorTests.class, MediumTests.class})
public class TestRegionServerObserver {
private static final Log LOG = LogFactory.getLog(TestRegionServerObserver.class);
/**
* Test verifies the hooks in regions merge.
* @throws Exception
*/
@Test
public void testCoprocessorHooksInRegionsMerge() throws Exception {
final int NUM_MASTERS = 1;
final int NUM_RS = 1;
final String TABLENAME = "testRegionServerObserver";
final String TABLENAME2 = "testRegionServerObserver_2";
final byte[] FAM = Bytes.toBytes("fam");
// Create config to use for this cluster
Configuration conf = HBaseConfiguration.create();
conf.setClass("hbase.coprocessor.regionserver.classes", CPRegionServerObserver.class,
RegionServerObserver.class);
// Start the cluster
HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(conf);
TEST_UTIL.startMiniCluster(NUM_MASTERS, NUM_RS);
Admin admin = new HBaseAdmin(conf);
try {
MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
HRegionServer regionServer = cluster.getRegionServer(0);
RegionServerCoprocessorHost cpHost = regionServer.getRegionServerCoprocessorHost();
Coprocessor coprocessor = cpHost.findCoprocessor(CPRegionServerObserver.class.getName());
CPRegionServerObserver regionServerObserver = (CPRegionServerObserver) coprocessor;
HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(TABLENAME));
desc.addFamily(new HColumnDescriptor(FAM));
admin.createTable(desc, new byte[][] { Bytes.toBytes("row") });
desc = new HTableDescriptor(TableName.valueOf(TABLENAME2));
desc.addFamily(new HColumnDescriptor(FAM));
admin.createTable(desc, new byte[][] { Bytes.toBytes("row") });
assertFalse(regionServerObserver.wasRegionMergeCalled());
List<HRegion> regions = regionServer.getOnlineRegions(TableName.valueOf(TABLENAME));
admin.mergeRegions(regions.get(0).getRegionInfo().getEncodedNameAsBytes(), regions.get(1)
.getRegionInfo().getEncodedNameAsBytes(), true);
int regionsCount = regionServer.getOnlineRegions(TableName.valueOf(TABLENAME)).size();
while (regionsCount != 1) {
regionsCount = regionServer.getOnlineRegions(TableName.valueOf(TABLENAME)).size();
Thread.sleep(1000);
}
assertTrue(regionServerObserver.wasRegionMergeCalled());
assertTrue(regionServerObserver.wasPreMergeCommit());
assertTrue(regionServerObserver.wasPostMergeCommit());
assertEquals(regionsCount, 1);
assertEquals(regionServer.getOnlineRegions(TableName.valueOf(TABLENAME2)).size(), 1);
} finally {
if (admin != null) admin.close();
TEST_UTIL.shutdownMiniCluster();
}
}
public static class CPRegionServerObserver extends BaseRegionServerObserver {
private RegionMergeTransaction rmt = null;
private HRegion mergedRegion = null;
private boolean preMergeCalled;
private boolean preMergeBeforePONRCalled;
private boolean preMergeAfterPONRCalled;
private boolean preRollBackMergeCalled;
private boolean postRollBackMergeCalled;
private boolean postMergeCalled;
public void resetStates() {
preMergeCalled = false;
preMergeBeforePONRCalled = false;
preMergeAfterPONRCalled = false;
preRollBackMergeCalled = false;
postRollBackMergeCalled = false;
postMergeCalled = false;
}
@Override
public void preMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx, HRegion regionA,
HRegion regionB) throws IOException {
preMergeCalled = true;
}
@Override
public void preMergeCommit(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
HRegion regionA, HRegion regionB, List<Mutation> metaEntries) throws IOException {
preMergeBeforePONRCalled = true;
RegionServerCoprocessorEnvironment environment = ctx.getEnvironment();
HRegionServer rs = (HRegionServer) environment.getRegionServerServices();
List<HRegion> onlineRegions =
rs.getOnlineRegions(TableName.valueOf("testRegionServerObserver_2"));
rmt = new RegionMergeTransaction(onlineRegions.get(0), onlineRegions.get(1), true);
if (!rmt.prepare(rs)) {
LOG.error("Prepare for the region merge of table "
+ onlineRegions.get(0).getTableDesc().getNameAsString()
+ " failed. So returning null. ");
ctx.bypass();
return;
}
mergedRegion = rmt.stepsBeforePONR(rs, rs, false);
rmt.prepareMutationsForMerge(mergedRegion.getRegionInfo(), regionA.getRegionInfo(),
regionB.getRegionInfo(), rs.getServerName(), metaEntries);
MetaTableAccessor.mutateMetaTable(rs.getShortCircuitConnection(), metaEntries);
}
@Override
public void postMergeCommit(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
HRegion regionA, HRegion regionB, HRegion mr) throws IOException {
preMergeAfterPONRCalled = true;
RegionServerCoprocessorEnvironment environment = ctx.getEnvironment();
HRegionServer rs = (HRegionServer) environment.getRegionServerServices();
rmt.stepsAfterPONR(rs, rs, this.mergedRegion);
}
@Override
public void preRollBackMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
HRegion regionA, HRegion regionB) throws IOException {
preRollBackMergeCalled = true;
}
@Override
public void postRollBackMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx,
HRegion regionA, HRegion regionB) throws IOException {
postRollBackMergeCalled = true;
}
@Override
public void postMerge(ObserverContext<RegionServerCoprocessorEnvironment> c, HRegion regionA,
HRegion regionB, HRegion mergedRegion) throws IOException {
postMergeCalled = true;
}
public boolean wasPreMergeCalled() {
return this.preMergeCalled;
}
public boolean wasPostMergeCalled() {
return this.postMergeCalled;
}
public boolean wasPreMergeCommit() {
return this.preMergeBeforePONRCalled;
}
public boolean wasPostMergeCommit() {
return this.preMergeAfterPONRCalled;
}
public boolean wasPreRollBackMerge() {
return this.preRollBackMergeCalled;
}
public boolean wasPostRollBackMerge() {
return this.postRollBackMergeCalled;
}
public boolean wasRegionMergeCalled() {
return this.preMergeCalled && this.postMergeCalled;
}
}
}