001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.util; 020 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.fail; 023 024import java.io.IOException; 025import java.util.ArrayList; 026import java.util.Collection; 027import java.util.EnumSet; 028import java.util.HashMap; 029import java.util.List; 030import java.util.Map; 031import java.util.Optional; 032import java.util.UUID; 033import java.util.concurrent.CountDownLatch; 034import java.util.concurrent.ExecutorService; 035import java.util.concurrent.ScheduledThreadPoolExecutor; 036import org.apache.hadoop.conf.Configuration; 037import org.apache.hadoop.fs.FileStatus; 038import org.apache.hadoop.fs.FileSystem; 039import org.apache.hadoop.fs.Path; 040import org.apache.hadoop.hbase.ClusterMetrics; 041import org.apache.hadoop.hbase.ClusterMetrics.Option; 042import org.apache.hadoop.hbase.HBaseTestingUtility; 043import org.apache.hadoop.hbase.HColumnDescriptor; 044import org.apache.hadoop.hbase.HConstants; 045import org.apache.hadoop.hbase.HRegionLocation; 046import org.apache.hadoop.hbase.HTableDescriptor; 047import org.apache.hadoop.hbase.ServerName; 048import org.apache.hadoop.hbase.TableName; 049import org.apache.hadoop.hbase.client.Admin; 050import org.apache.hadoop.hbase.client.ClusterConnection; 051import org.apache.hadoop.hbase.client.Connection; 052import org.apache.hadoop.hbase.client.ConnectionFactory; 053import org.apache.hadoop.hbase.client.Delete; 054import org.apache.hadoop.hbase.client.Put; 055import org.apache.hadoop.hbase.client.RegionInfo; 056import org.apache.hadoop.hbase.client.RegionLocator; 057import org.apache.hadoop.hbase.client.Scan; 058import org.apache.hadoop.hbase.client.Table; 059import org.apache.hadoop.hbase.client.TableDescriptor; 060import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 061import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 062import org.apache.hadoop.hbase.coprocessor.MasterObserver; 063import org.apache.hadoop.hbase.coprocessor.ObserverContext; 064import org.apache.hadoop.hbase.master.assignment.AssignmentManager; 065import org.apache.hadoop.hbase.master.assignment.RegionStates; 066import org.apache.hadoop.hbase.mob.MobFileName; 067import org.apache.hadoop.hbase.mob.MobUtils; 068import org.apache.hadoop.hbase.regionserver.HRegionFileSystem; 069import org.apache.hadoop.hbase.util.HBaseFsck.ErrorReporter; 070import org.apache.hadoop.hbase.util.HBaseFsck.HbckInfo; 071import org.apache.hadoop.hbase.util.HBaseFsck.TableInfo; 072import org.apache.hadoop.hbase.util.hbck.HFileCorruptionChecker; 073import org.apache.zookeeper.KeeperException; 074import org.junit.rules.TestName; 075import org.slf4j.Logger; 076import org.slf4j.LoggerFactory; 077 078import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 079import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos; 080 081/** 082 * This is the base class for HBaseFsck's ability to detect reasons for inconsistent tables. 083 * 084 * Actual tests are in : 085 * TestHBaseFsckTwoRS 086 * TestHBaseFsckOneRS 087 * TestHBaseFsckMOB 088 * TestHBaseFsckReplicas 089 */ 090public class BaseTestHBaseFsck { 091 static final int POOL_SIZE = 7; 092 protected static final Logger LOG = LoggerFactory.getLogger(BaseTestHBaseFsck.class); 093 protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 094 protected final static Configuration conf = TEST_UTIL.getConfiguration(); 095 protected final static String FAM_STR = "fam"; 096 protected final static byte[] FAM = Bytes.toBytes(FAM_STR); 097 protected final static int REGION_ONLINE_TIMEOUT = 800; 098 protected static AssignmentManager assignmentManager; 099 protected static RegionStates regionStates; 100 protected static ExecutorService tableExecutorService; 101 protected static ScheduledThreadPoolExecutor hbfsckExecutorService; 102 protected static ClusterConnection connection; 103 protected static Admin admin; 104 105 // for the instance, reset every test run 106 protected Table tbl; 107 protected final static byte[][] SPLITS = new byte[][] { Bytes.toBytes("A"), 108 Bytes.toBytes("B"), Bytes.toBytes("C") }; 109 // one row per region. 110 protected final static byte[][] ROWKEYS= new byte[][] { 111 Bytes.toBytes("00"), Bytes.toBytes("50"), Bytes.toBytes("A0"), Bytes.toBytes("A5"), 112 Bytes.toBytes("B0"), Bytes.toBytes("B5"), Bytes.toBytes("C0"), Bytes.toBytes("C5") }; 113 114 /** 115 * Debugging method to dump the contents of meta. 116 */ 117 protected void dumpMeta(TableName tableName) throws IOException { 118 List<byte[]> metaRows = TEST_UTIL.getMetaTableRows(tableName); 119 for (byte[] row : metaRows) { 120 LOG.info(Bytes.toString(row)); 121 } 122 } 123 124 /** 125 * This method is used to undeploy a region -- close it and attempt to 126 * remove its state from the Master. 127 */ 128 protected void undeployRegion(Connection conn, ServerName sn, 129 RegionInfo hri) throws IOException, InterruptedException { 130 try { 131 HBaseFsckRepair.closeRegionSilentlyAndWait(conn, sn, hri); 132 if (!hri.isMetaRegion()) { 133 admin.offline(hri.getRegionName()); 134 } 135 } catch (IOException ioe) { 136 LOG.warn("Got exception when attempting to offline region " 137 + Bytes.toString(hri.getRegionName()), ioe); 138 } 139 } 140 /** 141 * Delete a region from assignments, meta, or completely from hdfs. 142 * @param unassign if true unassign region if assigned 143 * @param metaRow if true remove region's row from META 144 * @param hdfs if true remove region's dir in HDFS 145 */ 146 protected void deleteRegion(Configuration conf, final HTableDescriptor htd, 147 byte[] startKey, byte[] endKey, boolean unassign, boolean metaRow, 148 boolean hdfs) throws IOException, InterruptedException { 149 deleteRegion(conf, htd, startKey, endKey, unassign, metaRow, hdfs, false, 150 RegionInfo.DEFAULT_REPLICA_ID); 151 } 152 153 /** 154 * Delete a region from assignments, meta, or completely from hdfs. 155 * @param unassign if true unassign region if assigned 156 * @param metaRow if true remove region's row from META 157 * @param hdfs if true remove region's dir in HDFS 158 * @param regionInfoOnly if true remove a region dir's .regioninfo file 159 * @param replicaId replica id 160 */ 161 protected void deleteRegion(Configuration conf, final HTableDescriptor htd, 162 byte[] startKey, byte[] endKey, boolean unassign, boolean metaRow, 163 boolean hdfs, boolean regionInfoOnly, int replicaId) 164 throws IOException, InterruptedException { 165 LOG.info("** Before delete:"); 166 dumpMeta(htd.getTableName()); 167 168 List<HRegionLocation> locations; 169 try(RegionLocator rl = connection.getRegionLocator(tbl.getName())) { 170 locations = rl.getAllRegionLocations(); 171 } 172 173 for (HRegionLocation location : locations) { 174 RegionInfo hri = location.getRegionInfo(); 175 ServerName hsa = location.getServerName(); 176 if (Bytes.compareTo(hri.getStartKey(), startKey) == 0 177 && Bytes.compareTo(hri.getEndKey(), endKey) == 0 178 && hri.getReplicaId() == replicaId) { 179 180 LOG.info("RegionName: " +hri.getRegionNameAsString()); 181 byte[] deleteRow = hri.getRegionName(); 182 183 if (unassign) { 184 LOG.info("Undeploying region " + hri + " from server " + hsa); 185 undeployRegion(connection, hsa, hri); 186 } 187 188 if (regionInfoOnly) { 189 LOG.info("deleting hdfs .regioninfo data: " + hri.toString() + hsa.toString()); 190 Path rootDir = FSUtils.getRootDir(conf); 191 FileSystem fs = rootDir.getFileSystem(conf); 192 Path p = new Path(FSUtils.getTableDir(rootDir, htd.getTableName()), 193 hri.getEncodedName()); 194 Path hriPath = new Path(p, HRegionFileSystem.REGION_INFO_FILE); 195 fs.delete(hriPath, true); 196 } 197 198 if (hdfs) { 199 LOG.info("deleting hdfs data: " + hri.toString() + hsa.toString()); 200 Path rootDir = FSUtils.getRootDir(conf); 201 FileSystem fs = rootDir.getFileSystem(conf); 202 Path p = new Path(FSUtils.getTableDir(rootDir, htd.getTableName()), 203 hri.getEncodedName()); 204 HBaseFsck.debugLsr(conf, p); 205 boolean success = fs.delete(p, true); 206 LOG.info("Deleted " + p + " sucessfully? " + success); 207 HBaseFsck.debugLsr(conf, p); 208 } 209 210 if (metaRow) { 211 try (Table meta = connection.getTable(TableName.META_TABLE_NAME, tableExecutorService)) { 212 Delete delete = new Delete(deleteRow); 213 meta.delete(delete); 214 } 215 } 216 } 217 LOG.info(hri.toString() + hsa.toString()); 218 } 219 220 TEST_UTIL.getMetaTableRows(htd.getTableName()); 221 LOG.info("*** After delete:"); 222 dumpMeta(htd.getTableName()); 223 } 224 225 /** 226 * Setup a clean table before we start mucking with it. 227 * 228 * It will set tbl which needs to be closed after test 229 * 230 * @throws IOException 231 * @throws InterruptedException 232 * @throws KeeperException 233 */ 234 void setupTable(TableName tablename) throws Exception { 235 setupTableWithRegionReplica(tablename, 1); 236 } 237 238 /** 239 * Setup a clean table with a certain region_replica count 240 * 241 * It will set tbl which needs to be closed after test 242 * 243 * @throws Exception 244 */ 245 void setupTableWithRegionReplica(TableName tablename, int replicaCount) throws Exception { 246 HTableDescriptor desc = new HTableDescriptor(tablename); 247 desc.setRegionReplication(replicaCount); 248 HColumnDescriptor hcd = new HColumnDescriptor(Bytes.toString(FAM)); 249 desc.addFamily(hcd); // If a table has no CF's it doesn't get checked 250 createTable(TEST_UTIL, desc, SPLITS); 251 252 tbl = connection.getTable(tablename, tableExecutorService); 253 List<Put> puts = new ArrayList<>(ROWKEYS.length); 254 for (byte[] row : ROWKEYS) { 255 Put p = new Put(row); 256 p.addColumn(FAM, Bytes.toBytes("val"), row); 257 puts.add(p); 258 } 259 tbl.put(puts); 260 } 261 262 /** 263 * Setup a clean table with a mob-enabled column. 264 * 265 * @param tablename The name of a table to be created. 266 * @throws Exception 267 */ 268 void setupMobTable(TableName tablename) throws Exception { 269 HTableDescriptor desc = new HTableDescriptor(tablename); 270 HColumnDescriptor hcd = new HColumnDescriptor(Bytes.toString(FAM)); 271 hcd.setMobEnabled(true); 272 hcd.setMobThreshold(0); 273 desc.addFamily(hcd); // If a table has no CF's it doesn't get checked 274 createTable(TEST_UTIL, desc, SPLITS); 275 276 tbl = connection.getTable(tablename, tableExecutorService); 277 List<Put> puts = new ArrayList<>(ROWKEYS.length); 278 for (byte[] row : ROWKEYS) { 279 Put p = new Put(row); 280 p.addColumn(FAM, Bytes.toBytes("val"), row); 281 puts.add(p); 282 } 283 tbl.put(puts); 284 } 285 286 /** 287 * Counts the number of rows to verify data loss or non-dataloss. 288 */ 289 int countRows() throws IOException { 290 return TEST_UTIL.countRows(tbl); 291 } 292 293 /** 294 * Counts the number of rows to verify data loss or non-dataloss. 295 */ 296 int countRows(byte[] start, byte[] end) throws IOException { 297 return TEST_UTIL.countRows(tbl, new Scan(start, end)); 298 } 299 300 /** 301 * delete table in preparation for next test 302 * 303 * @param tablename 304 * @throws IOException 305 */ 306 void cleanupTable(TableName tablename) throws Exception { 307 if (tbl != null) { 308 tbl.close(); 309 tbl = null; 310 } 311 312 ((ClusterConnection) connection).clearRegionCache(); 313 deleteTable(TEST_UTIL, tablename); 314 } 315 316 /** 317 * Get region info from local cluster. 318 */ 319 Map<ServerName, List<String>> getDeployedHRIs(final Admin admin) throws IOException { 320 ClusterMetrics status = admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)); 321 Collection<ServerName> regionServers = status.getLiveServerMetrics().keySet(); 322 Map<ServerName, List<String>> mm = new HashMap<>(); 323 for (ServerName hsi : regionServers) { 324 AdminProtos.AdminService.BlockingInterface server = connection.getAdmin(hsi); 325 326 // list all online regions from this region server 327 List<RegionInfo> regions = ProtobufUtil.getOnlineRegions(server); 328 List<String> regionNames = new ArrayList<>(regions.size()); 329 for (RegionInfo hri : regions) { 330 regionNames.add(hri.getRegionNameAsString()); 331 } 332 mm.put(hsi, regionNames); 333 } 334 return mm; 335 } 336 337 /** 338 * Returns the HSI a region info is on. 339 */ 340 ServerName findDeployedHSI(Map<ServerName, List<String>> mm, RegionInfo hri) { 341 for (Map.Entry<ServerName,List <String>> e : mm.entrySet()) { 342 if (e.getValue().contains(hri.getRegionNameAsString())) { 343 return e.getKey(); 344 } 345 } 346 return null; 347 } 348 349 public void deleteTableDir(TableName table) throws IOException { 350 Path rootDir = FSUtils.getRootDir(conf); 351 FileSystem fs = rootDir.getFileSystem(conf); 352 Path p = FSUtils.getTableDir(rootDir, table); 353 HBaseFsck.debugLsr(conf, p); 354 boolean success = fs.delete(p, true); 355 LOG.info("Deleted " + p + " sucessfully? " + success); 356 } 357 358 /** 359 * We don't have an easy way to verify that a flush completed, so we loop until we find a 360 * legitimate hfile and return it. 361 * @param fs 362 * @param table 363 * @return Path of a flushed hfile. 364 * @throws IOException 365 */ 366 Path getFlushedHFile(FileSystem fs, TableName table) throws IOException { 367 Path tableDir= FSUtils.getTableDir(FSUtils.getRootDir(conf), table); 368 Path regionDir = FSUtils.getRegionDirs(fs, tableDir).get(0); 369 Path famDir = new Path(regionDir, FAM_STR); 370 371 // keep doing this until we get a legit hfile 372 while (true) { 373 FileStatus[] hfFss = fs.listStatus(famDir); 374 if (hfFss.length == 0) { 375 continue; 376 } 377 for (FileStatus hfs : hfFss) { 378 if (!hfs.isDirectory()) { 379 return hfs.getPath(); 380 } 381 } 382 } 383 } 384 385 /** 386 * Gets flushed mob files. 387 * @param fs The current file system. 388 * @param table The current table name. 389 * @return Path of a flushed hfile. 390 * @throws IOException 391 */ 392 Path getFlushedMobFile(FileSystem fs, TableName table) throws IOException { 393 Path famDir = MobUtils.getMobFamilyPath(conf, table, FAM_STR); 394 395 // keep doing this until we get a legit hfile 396 while (true) { 397 FileStatus[] hfFss = fs.listStatus(famDir); 398 if (hfFss.length == 0) { 399 continue; 400 } 401 for (FileStatus hfs : hfFss) { 402 if (!hfs.isDirectory()) { 403 return hfs.getPath(); 404 } 405 } 406 } 407 } 408 409 /** 410 * Creates a new mob file name by the old one. 411 * @param oldFileName The old mob file name. 412 * @return The new mob file name. 413 */ 414 String createMobFileName(String oldFileName) { 415 MobFileName mobFileName = MobFileName.create(oldFileName); 416 String startKey = mobFileName.getStartKey(); 417 String date = mobFileName.getDate(); 418 return MobFileName.create(startKey, date, UUID.randomUUID().toString().replaceAll("-", "")) 419 .getFileName(); 420 } 421 422 423 424 425 /** 426 * Test that use this should have a timeout, because this method could potentially wait forever. 427 */ 428 protected void doQuarantineTest(TableName table, HBaseFsck hbck, int check, 429 int corrupt, int fail, int quar, int missing) throws Exception { 430 try { 431 setupTable(table); 432 assertEquals(ROWKEYS.length, countRows()); 433 admin.flush(table); // flush is async. 434 435 // Mess it up by leaving a hole in the assignment, meta, and hdfs data 436 admin.disableTable(table); 437 438 String[] args = {"-sidelineCorruptHFiles", "-repairHoles", "-ignorePreCheckPermission", 439 table.getNameAsString()}; 440 HBaseFsck res = hbck.exec(hbfsckExecutorService, args); 441 442 HFileCorruptionChecker hfcc = res.getHFilecorruptionChecker(); 443 assertEquals(hfcc.getHFilesChecked(), check); 444 assertEquals(hfcc.getCorrupted().size(), corrupt); 445 assertEquals(hfcc.getFailures().size(), fail); 446 assertEquals(hfcc.getQuarantined().size(), quar); 447 assertEquals(hfcc.getMissing().size(), missing); 448 449 // its been fixed, verify that we can enable 450 admin.enableTableAsync(table); 451 while (!admin.isTableEnabled(table)) { 452 try { 453 Thread.sleep(250); 454 } catch (InterruptedException e) { 455 e.printStackTrace(); 456 fail("Interrupted when trying to enable table " + table); 457 } 458 } 459 } finally { 460 cleanupTable(table); 461 } 462 } 463 464 465 static class MockErrorReporter implements ErrorReporter { 466 static int calledCount = 0; 467 468 @Override 469 public void clear() { 470 calledCount++; 471 } 472 473 @Override 474 public void report(String message) { 475 calledCount++; 476 } 477 478 @Override 479 public void reportError(String message) { 480 calledCount++; 481 } 482 483 @Override 484 public void reportError(ERROR_CODE errorCode, String message) { 485 calledCount++; 486 } 487 488 @Override 489 public void reportError(ERROR_CODE errorCode, String message, TableInfo table) { 490 calledCount++; 491 } 492 493 @Override 494 public void reportError(ERROR_CODE errorCode, 495 String message, TableInfo table, HbckInfo info) { 496 calledCount++; 497 } 498 499 @Override 500 public void reportError(ERROR_CODE errorCode, String message, 501 TableInfo table, HbckInfo info1, HbckInfo info2) { 502 calledCount++; 503 } 504 505 @Override 506 public int summarize() { 507 return ++calledCount; 508 } 509 510 @Override 511 public void detail(String details) { 512 calledCount++; 513 } 514 515 @Override 516 public ArrayList<ERROR_CODE> getErrorList() { 517 calledCount++; 518 return new ArrayList<>(); 519 } 520 521 @Override 522 public void progress() { 523 calledCount++; 524 } 525 526 @Override 527 public void print(String message) { 528 calledCount++; 529 } 530 531 @Override 532 public void resetErrors() { 533 calledCount++; 534 } 535 536 @Override 537 public boolean tableHasErrors(TableInfo table) { 538 calledCount++; 539 return false; 540 } 541 } 542 543 544 protected void deleteMetaRegion(Configuration conf, boolean unassign, boolean hdfs, 545 boolean regionInfoOnly) throws IOException, InterruptedException { 546 HRegionLocation metaLocation = connection.getRegionLocator(TableName.META_TABLE_NAME) 547 .getRegionLocation(HConstants.EMPTY_START_ROW); 548 ServerName hsa = metaLocation.getServerName(); 549 RegionInfo hri = metaLocation.getRegionInfo(); 550 if (unassign) { 551 LOG.info("Undeploying meta region " + hri + " from server " + hsa); 552 try (Connection unmanagedConnection = ConnectionFactory.createConnection(conf)) { 553 undeployRegion(unmanagedConnection, hsa, hri); 554 } 555 } 556 557 if (regionInfoOnly) { 558 LOG.info("deleting hdfs .regioninfo data: " + hri.toString() + hsa.toString()); 559 Path rootDir = FSUtils.getRootDir(conf); 560 FileSystem fs = rootDir.getFileSystem(conf); 561 Path p = new Path(rootDir + "/" + TableName.META_TABLE_NAME.getNameAsString(), 562 hri.getEncodedName()); 563 Path hriPath = new Path(p, HRegionFileSystem.REGION_INFO_FILE); 564 fs.delete(hriPath, true); 565 } 566 567 if (hdfs) { 568 LOG.info("deleting hdfs data: " + hri.toString() + hsa.toString()); 569 Path rootDir = FSUtils.getRootDir(conf); 570 FileSystem fs = rootDir.getFileSystem(conf); 571 Path p = new Path(rootDir + "/" + TableName.META_TABLE_NAME.getNameAsString(), 572 hri.getEncodedName()); 573 HBaseFsck.debugLsr(conf, p); 574 boolean success = fs.delete(p, true); 575 LOG.info("Deleted " + p + " sucessfully? " + success); 576 HBaseFsck.debugLsr(conf, p); 577 } 578 } 579 580 @org.junit.Rule 581 public TestName name = new TestName(); 582 583 public static class MasterSyncCoprocessor implements MasterCoprocessor, MasterObserver { 584 volatile CountDownLatch tableCreationLatch = null; 585 volatile CountDownLatch tableDeletionLatch = null; 586 587 @Override 588 public Optional<MasterObserver> getMasterObserver() { 589 return Optional.of(this); 590 } 591 592 @Override 593 public void postCompletedCreateTableAction( 594 final ObserverContext<MasterCoprocessorEnvironment> ctx, 595 final TableDescriptor desc, 596 final RegionInfo[] regions) throws IOException { 597 // the AccessController test, some times calls only and directly the 598 // postCompletedCreateTableAction() 599 if (tableCreationLatch != null) { 600 tableCreationLatch.countDown(); 601 } 602 } 603 604 @Override 605 public void postCompletedDeleteTableAction( 606 final ObserverContext<MasterCoprocessorEnvironment> ctx, 607 final TableName tableName) throws IOException { 608 // the AccessController test, some times calls only and directly the 609 // postCompletedDeleteTableAction() 610 if (tableDeletionLatch != null) { 611 tableDeletionLatch.countDown(); 612 } 613 } 614 } 615 616 public static void createTable(HBaseTestingUtility testUtil, HTableDescriptor htd, 617 byte [][] splitKeys) throws Exception { 618 // NOTE: We need a latch because admin is not sync, 619 // so the postOp coprocessor method may be called after the admin operation returned. 620 MasterSyncCoprocessor coproc = testUtil.getHBaseCluster().getMaster() 621 .getMasterCoprocessorHost().findCoprocessor(MasterSyncCoprocessor.class); 622 coproc.tableCreationLatch = new CountDownLatch(1); 623 if (splitKeys != null) { 624 admin.createTable(htd, splitKeys); 625 } else { 626 admin.createTable(htd); 627 } 628 coproc.tableCreationLatch.await(); 629 coproc.tableCreationLatch = null; 630 testUtil.waitUntilAllRegionsAssigned(htd.getTableName()); 631 } 632 633 public static void deleteTable(HBaseTestingUtility testUtil, TableName tableName) 634 throws Exception { 635 // NOTE: We need a latch because admin is not sync, 636 // so the postOp coprocessor method may be called after the admin operation returned. 637 MasterSyncCoprocessor coproc = testUtil.getHBaseCluster().getMaster() 638 .getMasterCoprocessorHost().findCoprocessor(MasterSyncCoprocessor.class); 639 coproc.tableDeletionLatch = new CountDownLatch(1); 640 try { 641 admin.disableTable(tableName); 642 } catch (Exception e) { 643 LOG.debug("Table: " + tableName + " already disabled, so just deleting it."); 644 } 645 admin.deleteTable(tableName); 646 coproc.tableDeletionLatch.await(); 647 coproc.tableDeletionLatch = null; 648 } 649}