001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.client; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertTrue; 022import static org.junit.Assert.fail; 023 024import java.io.IOException; 025import java.util.ArrayList; 026import java.util.List; 027import java.util.Optional; 028import java.util.concurrent.CountDownLatch; 029import org.apache.hadoop.hbase.HBaseClassTestRule; 030import org.apache.hadoop.hbase.HBaseTestingUtility; 031import org.apache.hadoop.hbase.HColumnDescriptor; 032import org.apache.hadoop.hbase.HConstants; 033import org.apache.hadoop.hbase.HRegionInfo; 034import org.apache.hadoop.hbase.HTableDescriptor; 035import org.apache.hadoop.hbase.MetaTableAccessor; 036import org.apache.hadoop.hbase.MiniHBaseCluster; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 039import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 040import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 041import org.apache.hadoop.hbase.coprocessor.MasterObserver; 042import org.apache.hadoop.hbase.coprocessor.ObserverContext; 043import org.apache.hadoop.hbase.master.HMaster; 044import org.apache.hadoop.hbase.testclassification.MasterTests; 045import org.apache.hadoop.hbase.testclassification.MediumTests; 046import org.apache.hadoop.hbase.util.Bytes; 047import org.apache.hadoop.hbase.util.JVMClusterUtil; 048import org.junit.After; 049import org.junit.Before; 050import org.junit.ClassRule; 051import org.junit.Rule; 052import org.junit.Test; 053import org.junit.experimental.categories.Category; 054import org.junit.rules.TestName; 055import org.slf4j.Logger; 056import org.slf4j.LoggerFactory; 057 058import org.apache.hbase.thirdparty.com.google.common.base.Predicate; 059import org.apache.hbase.thirdparty.com.google.common.collect.Iterables; 060import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 061 062@Category({ MasterTests.class, MediumTests.class }) 063public class TestEnableTable { 064 065 @ClassRule 066 public static final HBaseClassTestRule CLASS_RULE = 067 HBaseClassTestRule.forClass(TestEnableTable.class); 068 069 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 070 private static final Logger LOG = LoggerFactory.getLogger(TestEnableTable.class); 071 private static final byte[] FAMILYNAME = Bytes.toBytes("fam"); 072 073 @Rule 074 public TestName name = new TestName(); 075 076 @Before 077 public void setUp() throws Exception { 078 TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, 079 MasterSyncObserver.class.getName()); 080 TEST_UTIL.startMiniCluster(1); 081 } 082 083 @After 084 public void tearDown() throws Exception { 085 TEST_UTIL.shutdownMiniCluster(); 086 } 087 088 @Test 089 public void testEnableTableWithNoRegionServers() throws Exception { 090 final TableName tableName = TableName.valueOf(name.getMethodName()); 091 final MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); 092 final HMaster m = cluster.getMaster(); 093 final Admin admin = TEST_UTIL.getAdmin(); 094 final HTableDescriptor desc = new HTableDescriptor(tableName); 095 desc.addFamily(new HColumnDescriptor(FAMILYNAME)); 096 admin.createTable(desc); 097 admin.disableTable(tableName); 098 TEST_UTIL.waitTableDisabled(tableName.getName()); 099 100 admin.enableTable(tableName); 101 TEST_UTIL.waitTableEnabled(tableName); 102 // disable once more 103 admin.disableTable(tableName); 104 105 TEST_UTIL.waitUntilNoRegionsInTransition(60000); 106 // now stop region servers 107 JVMClusterUtil.RegionServerThread rs = cluster.getRegionServerThreads().get(0); 108 rs.getRegionServer().stop("stop"); 109 cluster.waitForRegionServerToStop(rs.getRegionServer().getServerName(), 10000); 110 111 // We used to enable the table here but AMv2 would hang waiting on a RS to check-in. 112 // Revisit. 113 114 JVMClusterUtil.RegionServerThread rs2 = cluster.startRegionServer(); 115 cluster.waitForRegionServerToStart(rs2.getRegionServer().getServerName().getHostname(), 116 rs2.getRegionServer().getServerName().getPort(), 60000); 117 118 LOG.debug("Now enabling table " + tableName); 119 admin.enableTable(tableName); 120 assertTrue(admin.isTableEnabled(tableName)); 121 122 List<HRegionInfo> regions = TEST_UTIL.getAdmin().getTableRegions(tableName); 123 assertEquals(1, regions.size()); 124 for (HRegionInfo region : regions) { 125 TEST_UTIL.getAdmin().assign(region.getEncodedNameAsBytes()); 126 } 127 LOG.debug("Waiting for table assigned " + tableName); 128 TEST_UTIL.waitUntilAllRegionsAssigned(tableName); 129 List<HRegionInfo> onlineRegions = admin.getOnlineRegions( 130 rs2.getRegionServer().getServerName()); 131 ArrayList<HRegionInfo> tableRegions = filterTableRegions(tableName, onlineRegions); 132 assertEquals(1, tableRegions.size()); 133 } 134 135 private ArrayList<HRegionInfo> filterTableRegions(final TableName tableName, 136 List<HRegionInfo> onlineRegions) { 137 return Lists.newArrayList(Iterables.filter(onlineRegions, new Predicate<HRegionInfo>() { 138 @Override 139 public boolean apply(HRegionInfo input) { 140 return input.getTable().equals(tableName); 141 } 142 })); 143 } 144 145 /** 146 * We were only clearing rows that had a hregioninfo column in hbase:meta. Mangled rows that 147 * were missing the hregioninfo because of error were being left behind messing up any 148 * subsequent table made with the same name. HBASE-12980 149 * @throws IOException 150 * @throws InterruptedException 151 */ 152 @Test 153 public void testDeleteForSureClearsAllTableRowsFromMeta() 154 throws IOException, InterruptedException { 155 final TableName tableName = TableName.valueOf(name.getMethodName()); 156 final Admin admin = TEST_UTIL.getAdmin(); 157 final HTableDescriptor desc = new HTableDescriptor(tableName); 158 desc.addFamily(new HColumnDescriptor(FAMILYNAME)); 159 try { 160 createTable(TEST_UTIL, desc, HBaseTestingUtility.KEYS_FOR_HBA_CREATE_TABLE); 161 } catch (Exception e) { 162 e.printStackTrace(); 163 fail("Got an exception while creating " + tableName); 164 } 165 // Now I have a nice table, mangle it by removing the HConstants.REGIONINFO_QUALIFIER_STR 166 // content from a few of the rows. 167 try (Table metaTable = TEST_UTIL.getConnection().getTable(TableName.META_TABLE_NAME)) { 168 try (ResultScanner scanner = metaTable.getScanner( 169 MetaTableAccessor.getScanForTableName(TEST_UTIL.getConnection(), tableName))) { 170 for (Result result : scanner) { 171 // Just delete one row. 172 Delete d = new Delete(result.getRow()); 173 d.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER); 174 LOG.info("Mangled: " + d); 175 metaTable.delete(d); 176 break; 177 } 178 } 179 admin.disableTable(tableName); 180 TEST_UTIL.waitTableDisabled(tableName.getName()); 181 // Rely on the coprocessor based latch to make the operation synchronous. 182 try { 183 deleteTable(TEST_UTIL, tableName); 184 } catch (Exception e) { 185 e.printStackTrace(); 186 fail("Got an exception while deleting " + tableName); 187 } 188 int rowCount = 0; 189 try (ResultScanner scanner = 190 metaTable.getScanner(MetaTableAccessor.getScanForTableName(TEST_UTIL.getConnection(), tableName))) { 191 for (Result result : scanner) { 192 LOG.info("Found when none expected: " + result); 193 rowCount++; 194 } 195 } 196 assertEquals(0, rowCount); 197 } 198 } 199 200 public static class MasterSyncObserver implements MasterCoprocessor, MasterObserver { 201 volatile CountDownLatch tableCreationLatch = null; 202 volatile CountDownLatch tableDeletionLatch = null; 203 204 @Override 205 public Optional<MasterObserver> getMasterObserver() { 206 return Optional.of(this); 207 } 208 209 @Override 210 public void postCompletedCreateTableAction( 211 final ObserverContext<MasterCoprocessorEnvironment> ctx, 212 final TableDescriptor desc, 213 final RegionInfo[] regions) throws IOException { 214 // the AccessController test, some times calls only and directly the 215 // postCompletedCreateTableAction() 216 if (tableCreationLatch != null) { 217 tableCreationLatch.countDown(); 218 } 219 } 220 221 @Override 222 public void postCompletedDeleteTableAction( 223 final ObserverContext<MasterCoprocessorEnvironment> ctx, 224 final TableName tableName) 225 throws IOException { 226 // the AccessController test, some times calls only and directly the postDeleteTableHandler() 227 if (tableDeletionLatch != null) { 228 tableDeletionLatch.countDown(); 229 } 230 } 231 } 232 233 public static void createTable(HBaseTestingUtility testUtil, 234 HTableDescriptor htd, byte [][] splitKeys) 235 throws Exception { 236 // NOTE: We need a latch because admin is not sync, 237 // so the postOp coprocessor method may be called after the admin operation returned. 238 MasterSyncObserver observer = testUtil.getHBaseCluster().getMaster() 239 .getMasterCoprocessorHost().findCoprocessor(MasterSyncObserver.class); 240 observer.tableCreationLatch = new CountDownLatch(1); 241 Admin admin = testUtil.getAdmin(); 242 if (splitKeys != null) { 243 admin.createTable(htd, splitKeys); 244 } else { 245 admin.createTable(htd); 246 } 247 observer.tableCreationLatch.await(); 248 observer.tableCreationLatch = null; 249 testUtil.waitUntilAllRegionsAssigned(htd.getTableName()); 250 } 251 252 public static void deleteTable(HBaseTestingUtility testUtil, TableName tableName) 253 throws Exception { 254 // NOTE: We need a latch because admin is not sync, 255 // so the postOp coprocessor method may be called after the admin operation returned. 256 MasterSyncObserver observer = testUtil.getHBaseCluster().getMaster() 257 .getMasterCoprocessorHost().findCoprocessor(MasterSyncObserver.class); 258 observer.tableDeletionLatch = new CountDownLatch(1); 259 Admin admin = testUtil.getAdmin(); 260 try { 261 admin.disableTable(tableName); 262 } catch (Exception e) { 263 LOG.debug("Table: " + tableName + " already disabled, so just deleting it."); 264 } 265 admin.deleteTable(tableName); 266 observer.tableDeletionLatch.await(); 267 observer.tableDeletionLatch = null; 268 } 269}