|
17 | 17 | */ |
18 | 18 | package org.apache.hadoop.hbase.tool; |
19 | 19 |
|
| 20 | +import static org.apache.hadoop.hbase.regionserver.TestRegionServerNoMaster.closeRegion; |
20 | 21 | import static org.junit.Assert.assertEquals; |
21 | 22 | import static org.junit.Assert.assertFalse; |
22 | 23 | import static org.junit.Assert.assertNotEquals; |
|
38 | 39 | import java.util.Map; |
39 | 40 | import java.util.concurrent.ExecutorService; |
40 | 41 | import java.util.concurrent.ScheduledThreadPoolExecutor; |
| 42 | +import java.util.concurrent.TimeUnit; |
41 | 43 | import org.apache.hadoop.conf.Configuration; |
42 | 44 | import org.apache.hadoop.hbase.HBaseClassTestRule; |
43 | 45 | import org.apache.hadoop.hbase.HBaseConfiguration; |
|
49 | 51 | import org.apache.hadoop.hbase.client.Put; |
50 | 52 | import org.apache.hadoop.hbase.client.RegionInfo; |
51 | 53 | import org.apache.hadoop.hbase.client.Table; |
| 54 | +import org.apache.hadoop.hbase.regionserver.HRegionServer; |
52 | 55 | import org.apache.hadoop.hbase.testclassification.LargeTests; |
53 | 56 | import org.apache.hadoop.hbase.util.Bytes; |
54 | 57 | import org.apache.hadoop.util.ToolRunner; |
@@ -130,6 +133,51 @@ public void testBasicCanaryWorks() throws Exception { |
130 | 133 | isA(ColumnFamilyDescriptor.class), anyLong()); |
131 | 134 | } |
132 | 135 |
|
| 136 | + /** |
| 137 | + * When CanaryTool times out, it should stop scanning and shutdown quickly and gracefully. This |
| 138 | + * test helps to confirm that threadpools do not continue executing work after the canary |
| 139 | + * finishes. It also verifies sink behavior and measures correct failure counts in the sink. |
| 140 | + * @throws Exception if it can't create a table, communicate with minicluster, or run the canary. |
| 141 | + */ |
| 142 | + @Test |
| 143 | + public void testCanaryStopsScanningAfterTimeout() throws Exception { |
| 144 | + // Prepare a table with multiple regions, and close those regions on the regionserver. |
| 145 | + // Do not notify HMaster or META. CanaryTool will scan and receive NotServingRegionExceptions. |
| 146 | + final TableName tableName = TableName.valueOf(name.getMethodName()); |
| 147 | + // Close the unused Table reference returned by createMultiRegionTable. |
| 148 | + testingUtility.createMultiRegionTable(tableName, new byte[][] { FAMILY }).close(); |
| 149 | + List<RegionInfo> regions = testingUtility.getAdmin().getRegions(tableName); |
| 150 | + assertTrue("verify table has multiple regions", regions.size() > 1); |
| 151 | + HRegionServer regionserver = testingUtility.getMiniHBaseCluster().getRegionServer(0); |
| 152 | + for (RegionInfo region : regions) { |
| 153 | + closeRegion(testingUtility, regionserver, region); |
| 154 | + } |
| 155 | + |
| 156 | + // Run CanaryTool with 1 thread. This thread will attempt to scan the first region. |
| 157 | + // It will use default rpc retries and receive NotServingRegionExceptions for many seconds |
| 158 | + // according to HConstants.RETRY_BACKOFF. The CanaryTool timeout is set to 4 seconds, so it |
| 159 | + // will time out before the first region scan is complete. |
| 160 | + ExecutorService executor = new ScheduledThreadPoolExecutor(1); |
| 161 | + CanaryTool canary = new CanaryTool(executor); |
| 162 | + String[] args = { "-t", "4000", tableName.getNameAsString() }; |
| 163 | + int retCode = ToolRunner.run(testingUtility.getConfiguration(), canary, args); |
| 164 | + executor.shutdown(); |
| 165 | + try { |
| 166 | + if (!executor.awaitTermination(3, TimeUnit.SECONDS)) { |
| 167 | + executor.shutdownNow(); |
| 168 | + } |
| 169 | + } catch (InterruptedException e) { |
| 170 | + executor.shutdownNow(); |
| 171 | + } |
| 172 | + |
| 173 | + CanaryTool.Sink sink = canary.getActiveSink(); |
| 174 | + assertEquals("verify canary timed out with TIMEOUT_ERROR_EXIT_CODE", 3, retCode); |
| 175 | + assertEquals("verify only the first region failed", 1, sink.getReadFailureCount()); |
| 176 | + assertEquals("verify no successful reads", 0, sink.getReadSuccessCount()); |
| 177 | + assertEquals("verify we were attempting to scan all regions", regions.size(), |
| 178 | + ((CanaryTool.RegionStdOutSink) sink).getTotalExpectedRegions()); |
| 179 | + } |
| 180 | + |
133 | 181 | @Test |
134 | 182 | public void testCanaryRegionTaskReadAllCF() throws Exception { |
135 | 183 | final TableName tableName = TableName.valueOf(name.getMethodName()); |
|
0 commit comments