diff --git hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestClientScannerRPCTimeout.java hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestClientScannerRPCTimeout.java index 803a81da3a..33b23f0363 100644 --- hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestClientScannerRPCTimeout.java +++ hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestClientScannerRPCTimeout.java @@ -26,7 +26,9 @@ import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.impl.Log4JLogger; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.testclassification.MediumTests; import org.apache.hadoop.hbase.MiniHBaseCluster.MiniHBaseClusterRegionServer; import org.apache.hadoop.hbase.TableName; @@ -46,6 +48,15 @@ import org.junit.experimental.categories.Category; import com.google.protobuf.RpcController; import com.google.protobuf.ServiceException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.coprocessor.BaseMasterObserver; +import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; +import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; +import org.apache.hadoop.hbase.coprocessor.ObserverContext; /** * Test the scenario where a HRegionServer#scan() call, while scanning, timeout at client side and @@ -60,6 +71,8 @@ public class TestClientScannerRPCTimeout { private static final byte[] VALUE = Bytes.toBytes("testValue"); private static final int rpcTimeout = 2 * 1000; private static final int CLIENT_RETRIES_NUMBER = 3; + private static final TableName FAILED_TABLE = TableName.valueOf("FAILED_TABLE"); + private static final TableName SLOW_TABLE = TableName.valueOf("SLOW_TABLE"); @BeforeClass public static void setUpBeforeClass() throws Exception { @@ -73,6 +86,7 @@ public class TestClientScannerRPCTimeout { conf.setStrings(HConstants.REGION_SERVER_IMPL, RegionServerWithScanTimeout.class.getName()); conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, CLIENT_RETRIES_NUMBER); conf.setInt(HConstants.HBASE_CLIENT_PAUSE, 1000); + conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, MyMasterObserver.class.getName()); TEST_UTIL.startMiniCluster(1); } @@ -82,6 +96,32 @@ public class TestClientScannerRPCTimeout { } @Test + public void testAdminUnderTimeoutAndException() throws Exception { + testAdminUnderTimeout(true); + } + + @Test + public void testAdminUnderTimeout() throws Exception { + testAdminUnderTimeout(false); + } + + private void testAdminUnderTimeout(boolean withException) throws Exception { + TableName name = withException ? FAILED_TABLE : SLOW_TABLE; + MyMasterObserver.ENABLE.set(true); + try { + Admin admin = TEST_UTIL.getHBaseAdmin(); + HTableDescriptor desc = new HTableDescriptor(name); + desc.addFamily(new HColumnDescriptor(FAMILY)); + admin.createTable(desc); + admin.truncateTable(name, false); + admin.disableTable(desc.getTableName()); + admin.deleteTable(desc.getTableName()); + } finally { + MyMasterObserver.ENABLE.set(false); + } + } + + @Test public void testScannerNextRPCTimesout() throws Exception { final TableName TABLE_NAME = TableName.valueOf("testScannerNextRPCTimesout"); Table ht = TEST_UTIL.createTable(TABLE_NAME, FAMILY); @@ -186,4 +226,84 @@ public class TestClientScannerRPCTimeout { } } } + + private static class MyMasterObserverFlag { + + private final String methodName; + private final AtomicBoolean hadSlept = new AtomicBoolean(false); + private final AtomicBoolean hadFailed = new AtomicBoolean(false); + + MyMasterObserverFlag(final String methodName) { + this.methodName = methodName; + } + + boolean match(final String methodName) { + return this.methodName.equals(methodName); + } + + boolean needSleep(TableName name) { + return name.equals(SLOW_TABLE) && hadSlept.compareAndSet(false, true); + } + + boolean needFailure(TableName name) { + return name.equals(FAILED_TABLE) && hadFailed.compareAndSet(false, true); + } + } + + public static class MyMasterObserver extends BaseMasterObserver { + private static final AtomicBoolean ENABLE = new AtomicBoolean(false); + private final List flags = Arrays.asList( + new MyMasterObserverFlag("preCreateTable"), + new MyMasterObserverFlag("preDeleteTable"), + new MyMasterObserverFlag("preDisableTable"), + new MyMasterObserverFlag("preTruncateTable") + ); + + private void checkFlag(final TableName name, final String methodName) throws IOException { + if (!ENABLE.get()) { + return; + } + for (MyMasterObserverFlag flag : flags) { + if (!flag.match(methodName)) { + continue; + } + if (flag.needSleep(name)) { + try { + TimeUnit.MILLISECONDS.sleep(rpcTimeout + 1000); + } catch (InterruptedException ex) { + throw new IOException(ex); + } + } + if (flag.needFailure(name)) { + throw new IOException( + "Exception was like a box of chocolates. You never know what you’re gonna get."); + } + } + } + + @Override + public void preCreateTable(ObserverContext ctx, + HTableDescriptor desc, HRegionInfo[] regions) throws IOException { + checkFlag(desc.getTableName(), "preCreateTable"); + } + + @Override + public void preDeleteTable(ObserverContext ctx, + TableName tableName) throws IOException { + checkFlag(tableName, "preDeleteTable"); + } + + @Override + public void preDisableTable(ObserverContext ctx, + TableName tableName) throws IOException { + checkFlag(tableName, "preDisableTable"); + } + + @Override + public void preTruncateTable(ObserverContext ctx, + TableName tableName) throws IOException { + checkFlag(tableName, "preTruncateTable"); + } + + } } \ No newline at end of file