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.apache.hadoop.hbase.client.metrics.ScanMetrics.REGIONS_SCANNED_METRIC_NAME; 021import static org.apache.hadoop.hbase.client.metrics.ServerSideScanMetrics.COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME; 022 023import java.io.IOException; 024import java.util.Arrays; 025import java.util.List; 026import java.util.Map; 027import java.util.stream.Collectors; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.fs.FileStatus; 030import org.apache.hadoop.fs.FileSystem; 031import org.apache.hadoop.fs.Path; 032import org.apache.hadoop.hbase.Cell; 033import org.apache.hadoop.hbase.CellScanner; 034import org.apache.hadoop.hbase.HBaseClassTestRule; 035import org.apache.hadoop.hbase.HBaseTestingUtil; 036import org.apache.hadoop.hbase.StartTestingClusterOption; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.client.metrics.ScanMetrics; 039import org.apache.hadoop.hbase.client.metrics.ScanMetricsRegionInfo; 040import org.apache.hadoop.hbase.master.cleaner.TimeToLiveHFileCleaner; 041import org.apache.hadoop.hbase.master.snapshot.SnapshotManager; 042import org.apache.hadoop.hbase.regionserver.HRegion; 043import org.apache.hadoop.hbase.regionserver.HRegionFileSystem; 044import org.apache.hadoop.hbase.regionserver.HRegionServer; 045import org.apache.hadoop.hbase.regionserver.StoreContext; 046import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTracker; 047import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory; 048import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper; 049import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils; 050import org.apache.hadoop.hbase.testclassification.ClientTests; 051import org.apache.hadoop.hbase.testclassification.LargeTests; 052import org.apache.hadoop.hbase.util.Bytes; 053import org.apache.hadoop.hbase.util.CommonFSUtils; 054import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 055import org.apache.hadoop.hbase.util.FSUtils; 056import org.apache.hadoop.hbase.util.HFileArchiveUtil; 057import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; 058import org.junit.After; 059import org.junit.Assert; 060import org.junit.Before; 061import org.junit.ClassRule; 062import org.junit.Rule; 063import org.junit.Test; 064import org.junit.experimental.categories.Category; 065import org.junit.rules.TestName; 066import org.slf4j.Logger; 067import org.slf4j.LoggerFactory; 068 069@Category({ LargeTests.class, ClientTests.class }) 070public class TestTableSnapshotScanner { 071 072 @ClassRule 073 public static final HBaseClassTestRule CLASS_RULE = 074 HBaseClassTestRule.forClass(TestTableSnapshotScanner.class); 075 076 private static final Logger LOG = LoggerFactory.getLogger(TestTableSnapshotScanner.class); 077 private final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 078 private static final int NUM_REGION_SERVERS = 2; 079 private static final byte[][] FAMILIES = { Bytes.toBytes("f1"), Bytes.toBytes("f2") }; 080 public static byte[] bbb = Bytes.toBytes("bbb"); 081 public static byte[] yyy = Bytes.toBytes("yyy"); 082 083 private FileSystem fs; 084 private Path rootDir; 085 private boolean clusterUp; 086 087 @Rule 088 public TestName name = new TestName(); 089 090 public static void blockUntilSplitFinished(HBaseTestingUtil util, TableName tableName, 091 int expectedRegionSize) throws Exception { 092 for (int i = 0; i < 100; i++) { 093 List<RegionInfo> hRegionInfoList = util.getAdmin().getRegions(tableName); 094 if (hRegionInfoList.size() >= expectedRegionSize) { 095 break; 096 } 097 Thread.sleep(1000); 098 } 099 } 100 101 @Before 102 public void setupCluster() throws Exception { 103 setupConf(UTIL.getConfiguration()); 104 StartTestingClusterOption option = 105 StartTestingClusterOption.builder().numRegionServers(NUM_REGION_SERVERS) 106 .numDataNodes(NUM_REGION_SERVERS).createRootDir(true).build(); 107 UTIL.startMiniCluster(option); 108 clusterUp = true; 109 rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir(); 110 fs = rootDir.getFileSystem(UTIL.getConfiguration()); 111 } 112 113 @After 114 public void tearDownCluster() throws Exception { 115 if (clusterUp) { 116 UTIL.shutdownMiniCluster(); 117 } 118 } 119 120 protected void setupConf(Configuration conf) { 121 // Enable snapshot 122 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true); 123 } 124 125 public static void createTableAndSnapshot(HBaseTestingUtil util, TableName tableName, 126 String snapshotName, int numRegions) throws Exception { 127 try { 128 util.deleteTable(tableName); 129 } catch (Exception ex) { 130 // ignore 131 } 132 133 if (numRegions > 1) { 134 util.createTable(tableName, FAMILIES, 1, bbb, yyy, numRegions); 135 } else { 136 util.createTable(tableName, FAMILIES); 137 } 138 Admin admin = util.getAdmin(); 139 140 // put some stuff in the table 141 Table table = util.getConnection().getTable(tableName); 142 util.loadTable(table, FAMILIES); 143 144 Path rootDir = CommonFSUtils.getRootDir(util.getConfiguration()); 145 FileSystem fs = rootDir.getFileSystem(util.getConfiguration()); 146 147 SnapshotTestingUtils.createSnapshotAndValidate(admin, tableName, Arrays.asList(FAMILIES), null, 148 snapshotName, rootDir, fs, true); 149 150 // load different values 151 byte[] value = Bytes.toBytes("after_snapshot_value"); 152 util.loadTable(table, FAMILIES, value); 153 154 // cause flush to create new files in the region 155 admin.flush(tableName); 156 table.close(); 157 } 158 159 @Test 160 public void testNoDuplicateResultsWhenSplitting() throws Exception { 161 TableName tableName = TableName.valueOf("testNoDuplicateResultsWhenSplitting"); 162 String snapshotName = "testSnapshotBug"; 163 try { 164 if (UTIL.getAdmin().tableExists(tableName)) { 165 UTIL.deleteTable(tableName); 166 } 167 168 UTIL.createTable(tableName, FAMILIES); 169 Admin admin = UTIL.getAdmin(); 170 171 // put some stuff in the table 172 Table table = UTIL.getConnection().getTable(tableName); 173 UTIL.loadTable(table, FAMILIES); 174 175 // split to 2 regions 176 admin.split(tableName, Bytes.toBytes("eee")); 177 blockUntilSplitFinished(UTIL, tableName, 2); 178 179 Path rootDir = CommonFSUtils.getRootDir(UTIL.getConfiguration()); 180 FileSystem fs = rootDir.getFileSystem(UTIL.getConfiguration()); 181 182 SnapshotTestingUtils.createSnapshotAndValidate(admin, tableName, Arrays.asList(FAMILIES), 183 null, snapshotName, rootDir, fs, true); 184 185 // load different values 186 byte[] value = Bytes.toBytes("after_snapshot_value"); 187 UTIL.loadTable(table, FAMILIES, value); 188 189 // cause flush to create new files in the region 190 admin.flush(tableName); 191 table.close(); 192 193 Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName); 194 Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan 195 196 TableSnapshotScanner scanner = 197 new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan); 198 199 verifyScanner(scanner, bbb, yyy); 200 scanner.close(); 201 } catch (Exception e) { 202 e.printStackTrace(); 203 } finally { 204 UTIL.getAdmin().deleteSnapshot(snapshotName); 205 UTIL.deleteTable(tableName); 206 } 207 } 208 209 @Test 210 public void testScanLimit() throws Exception { 211 final TableName tableName = TableName.valueOf(name.getMethodName()); 212 final String snapshotName = tableName + "Snapshot"; 213 TableSnapshotScanner scanner = null; 214 try { 215 createTableAndSnapshot(UTIL, tableName, snapshotName, 50); 216 Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName); 217 Scan scan = new Scan().withStartRow(bbb).setLimit(100); // limit the scan 218 219 scanner = new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan); 220 int count = 0; 221 while (true) { 222 Result result = scanner.next(); 223 if (result == null) { 224 break; 225 } 226 count++; 227 } 228 Assert.assertEquals(100, count); 229 } finally { 230 if (scanner != null) { 231 scanner.close(); 232 } 233 UTIL.getAdmin().deleteSnapshot(snapshotName); 234 UTIL.deleteTable(tableName); 235 } 236 } 237 238 @Test 239 public void testWithSingleRegion() throws Exception { 240 testScanner(UTIL, "testWithSingleRegion", 1, false); 241 } 242 243 @Test 244 public void testWithMultiRegion() throws Exception { 245 testScanner(UTIL, "testWithMultiRegion", 10, false); 246 } 247 248 @Test 249 public void testWithOfflineHBaseMultiRegion() throws Exception { 250 testScanner(UTIL, "testWithMultiRegion", 20, true); 251 } 252 253 private ScanMetrics createTableSnapshotScannerAndGetScanMetrics(boolean enableScanMetrics, 254 boolean enableScanMetricsByRegion, byte[] endKey) throws Exception { 255 TableName tableName = TableName.valueOf(name.getMethodName() + "_TABLE"); 256 String snapshotName = name.getMethodName() + "_SNAPSHOT"; 257 try { 258 createTableAndSnapshot(UTIL, tableName, snapshotName, 50); 259 Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName); 260 Scan scan = new Scan().withStartRow(bbb).withStopRow(endKey); 261 scan.setScanMetricsEnabled(enableScanMetrics); 262 scan.setEnableScanMetricsByRegion(enableScanMetricsByRegion); 263 Configuration conf = UTIL.getConfiguration(); 264 265 TableSnapshotScanner snapshotScanner = 266 new TableSnapshotScanner(conf, restoreDir, snapshotName, scan); 267 verifyScanner(snapshotScanner, bbb, endKey); 268 return snapshotScanner.getScanMetrics(); 269 } finally { 270 UTIL.getAdmin().deleteSnapshot(snapshotName); 271 UTIL.deleteTable(tableName); 272 } 273 } 274 275 @Test 276 public void testScanMetricsDisabled() throws Exception { 277 ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(false, false, yyy); 278 Assert.assertNull(scanMetrics); 279 } 280 281 @Test 282 public void testScanMetricsWithScanMetricsByRegionDisabled() throws Exception { 283 ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, false, yyy); 284 Assert.assertNotNull(scanMetrics); 285 int rowsScanned = 0; 286 for (byte[] row : HBaseTestingUtil.ROWS) { 287 if (Bytes.compareTo(row, bbb) >= 0 && Bytes.compareTo(row, yyy) < 0) { 288 rowsScanned++; 289 } 290 } 291 Map<String, Long> metricsMap = scanMetrics.getMetricsMap(); 292 Assert.assertEquals(rowsScanned, (long) metricsMap.get(COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME)); 293 } 294 295 @Test 296 public void testScanMetricsByRegionForSingleRegion() throws Exception { 297 // Scan single row with row key bbb 298 byte[] bbc = Bytes.toBytes("bbc"); 299 ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, true, bbc); 300 Assert.assertNotNull(scanMetrics); 301 Map<ScanMetricsRegionInfo, Map<String, Long>> scanMetricsByRegion = 302 scanMetrics.collectMetricsByRegion(); 303 Assert.assertEquals(1, scanMetricsByRegion.size()); 304 for (Map.Entry<ScanMetricsRegionInfo, Map<String, Long>> entry : scanMetricsByRegion 305 .entrySet()) { 306 ScanMetricsRegionInfo scanMetricsRegionInfo = entry.getKey(); 307 Map<String, Long> metricsMap = entry.getValue(); 308 Assert.assertNull(scanMetricsRegionInfo.getServerName()); 309 Assert.assertNotNull(scanMetricsRegionInfo.getEncodedRegionName()); 310 Assert.assertEquals(1, (long) metricsMap.get(REGIONS_SCANNED_METRIC_NAME)); 311 Assert.assertEquals(1, (long) metricsMap.get(COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME)); 312 } 313 } 314 315 @Test 316 public void testScanMetricsByRegionForMultiRegion() throws Exception { 317 ScanMetrics scanMetrics = createTableSnapshotScannerAndGetScanMetrics(true, true, yyy); 318 Assert.assertNotNull(scanMetrics); 319 Map<ScanMetricsRegionInfo, Map<String, Long>> scanMetricsByRegion = 320 scanMetrics.collectMetricsByRegion(); 321 for (Map.Entry<ScanMetricsRegionInfo, Map<String, Long>> entry : scanMetricsByRegion 322 .entrySet()) { 323 ScanMetricsRegionInfo scanMetricsRegionInfo = entry.getKey(); 324 Map<String, Long> metricsMap = entry.getValue(); 325 Assert.assertNull(scanMetricsRegionInfo.getServerName()); 326 Assert.assertNotNull(scanMetricsRegionInfo.getEncodedRegionName()); 327 Assert.assertEquals(1, (long) metricsMap.get(REGIONS_SCANNED_METRIC_NAME)); 328 } 329 } 330 331 @Test 332 public void testScannerWithRestoreScanner() throws Exception { 333 TableName tableName = TableName.valueOf("testScanner"); 334 String snapshotName = "testScannerWithRestoreScanner"; 335 try { 336 createTableAndSnapshot(UTIL, tableName, snapshotName, 50); 337 Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName); 338 Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan 339 340 Configuration conf = UTIL.getConfiguration(); 341 Path rootDir = CommonFSUtils.getRootDir(conf); 342 343 TableSnapshotScanner scanner0 = 344 new TableSnapshotScanner(conf, restoreDir, snapshotName, scan); 345 verifyScanner(scanner0, bbb, yyy); 346 scanner0.close(); 347 348 // restore snapshot. 349 RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName); 350 351 // scan the snapshot without restoring snapshot 352 TableSnapshotScanner scanner = 353 new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true); 354 verifyScanner(scanner, bbb, yyy); 355 scanner.close(); 356 357 // check whether the snapshot has been deleted by the close of scanner. 358 scanner = new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true); 359 verifyScanner(scanner, bbb, yyy); 360 scanner.close(); 361 362 // restore snapshot again. 363 RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName); 364 365 // check whether the snapshot has been deleted by the close of scanner. 366 scanner = new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true); 367 verifyScanner(scanner, bbb, yyy); 368 scanner.close(); 369 } finally { 370 UTIL.getAdmin().deleteSnapshot(snapshotName); 371 UTIL.deleteTable(tableName); 372 } 373 } 374 375 private void testScanner(HBaseTestingUtil util, String snapshotName, int numRegions, 376 boolean shutdownCluster) throws Exception { 377 TableName tableName = TableName.valueOf("testScanner"); 378 try { 379 createTableAndSnapshot(util, tableName, snapshotName, numRegions); 380 381 if (shutdownCluster) { 382 util.shutdownMiniHBaseCluster(); 383 clusterUp = false; 384 } 385 386 Path restoreDir = util.getDataTestDirOnTestFS(snapshotName); 387 Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan 388 389 TableSnapshotScanner scanner = 390 new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan); 391 392 verifyScanner(scanner, bbb, yyy); 393 scanner.close(); 394 } finally { 395 if (clusterUp) { 396 util.getAdmin().deleteSnapshot(snapshotName); 397 util.deleteTable(tableName); 398 } 399 } 400 } 401 402 private void verifyScanner(ResultScanner scanner, byte[] startRow, byte[] stopRow) 403 throws IOException, InterruptedException { 404 405 HBaseTestingUtil.SeenRowTracker rowTracker = 406 new HBaseTestingUtil.SeenRowTracker(startRow, stopRow); 407 408 while (true) { 409 Result result = scanner.next(); 410 if (result == null) { 411 break; 412 } 413 verifyRow(result); 414 rowTracker.addRow(result.getRow()); 415 } 416 417 // validate all rows are seen 418 rowTracker.validate(); 419 } 420 421 private static void verifyRow(Result result) throws IOException { 422 byte[] row = result.getRow(); 423 CellScanner scanner = result.cellScanner(); 424 while (scanner.advance()) { 425 Cell cell = scanner.current(); 426 427 // assert that all Cells in the Result have the same key 428 Assert.assertEquals(0, Bytes.compareTo(row, 0, row.length, cell.getRowArray(), 429 cell.getRowOffset(), cell.getRowLength())); 430 } 431 432 for (int j = 0; j < FAMILIES.length; j++) { 433 byte[] actual = result.getValue(FAMILIES[j], FAMILIES[j]); 434 Assert.assertArrayEquals("Row in snapshot does not match, expected:" + Bytes.toString(row) 435 + " ,actual:" + Bytes.toString(actual), row, actual); 436 } 437 } 438 439 @Test 440 public void testMergeRegion() throws Exception { 441 TableName tableName = TableName.valueOf("testMergeRegion"); 442 String snapshotName = tableName.getNameAsString() + "_snapshot"; 443 Configuration conf = UTIL.getConfiguration(); 444 Path rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir(); 445 long timeout = 20000; // 20s 446 try (Admin admin = UTIL.getAdmin()) { 447 List<String> serverList = admin.getRegionServers().stream().map(sn -> sn.getServerName()) 448 .collect(Collectors.toList()); 449 // create table with 3 regions 450 Table table = UTIL.createTable(tableName, FAMILIES, 1, bbb, yyy, 3); 451 List<RegionInfo> regions = admin.getRegions(tableName); 452 Assert.assertEquals(3, regions.size()); 453 RegionInfo region0 = regions.get(0); 454 RegionInfo region1 = regions.get(1); 455 RegionInfo region2 = regions.get(2); 456 // put some data in the table 457 UTIL.loadTable(table, FAMILIES); 458 admin.flush(tableName); 459 // wait flush is finished 460 UTIL.waitFor(timeout, () -> { 461 try { 462 Path tableDir = CommonFSUtils.getTableDir(rootDir, tableName); 463 for (RegionInfo region : regions) { 464 Path regionDir = new Path(tableDir, region.getEncodedName()); 465 for (Path familyDir : FSUtils.getFamilyDirs(fs, regionDir)) { 466 for (FileStatus fs : fs.listStatus(familyDir)) { 467 if (!fs.getPath().getName().equals(".filelist")) { 468 return true; 469 } 470 } 471 return false; 472 } 473 } 474 return true; 475 } catch (IOException e) { 476 LOG.warn("Failed check if flush is finished", e); 477 return false; 478 } 479 }); 480 // merge 2 regions 481 admin.compactionSwitch(false, serverList); 482 admin.mergeRegionsAsync(region0.getEncodedNameAsBytes(), region1.getEncodedNameAsBytes(), 483 true); 484 UTIL.waitFor(timeout, () -> admin.getRegions(tableName).size() == 2); 485 List<RegionInfo> mergedRegions = admin.getRegions(tableName); 486 RegionInfo mergedRegion = 487 mergedRegions.get(0).getEncodedName().equals(region2.getEncodedName()) 488 ? mergedRegions.get(1) 489 : mergedRegions.get(0); 490 // snapshot 491 admin.snapshot(snapshotName, tableName); 492 Assert.assertEquals(1, admin.listSnapshots().size()); 493 // major compact 494 admin.compactionSwitch(true, serverList); 495 admin.majorCompactRegion(mergedRegion.getRegionName()); 496 // wait until merged region has no reference 497 UTIL.waitFor(timeout, () -> { 498 try { 499 for (RegionServerThread regionServerThread : UTIL.getMiniHBaseCluster() 500 .getRegionServerThreads()) { 501 HRegionServer regionServer = regionServerThread.getRegionServer(); 502 for (HRegion subRegion : regionServer.getRegions(tableName)) { 503 if ( 504 subRegion.getRegionInfo().getEncodedName().equals(mergedRegion.getEncodedName()) 505 ) { 506 regionServer.getCompactedHFilesDischarger().chore(); 507 } 508 } 509 } 510 Path tableDir = CommonFSUtils.getTableDir(rootDir, tableName); 511 HRegionFileSystem regionFs = HRegionFileSystem 512 .openRegionFromFileSystem(UTIL.getConfiguration(), fs, tableDir, mergedRegion, true); 513 boolean references = false; 514 Path regionDir = new Path(tableDir, mergedRegion.getEncodedName()); 515 for (Path familyDir : FSUtils.getFamilyDirs(fs, regionDir)) { 516 StoreContext storeContext = StoreContext.getBuilder() 517 .withColumnFamilyDescriptor(ColumnFamilyDescriptorBuilder.of(familyDir.getName())) 518 .withRegionFileSystem(regionFs).withFamilyStoreDirectoryPath(familyDir).build(); 519 StoreFileTracker sft = 520 StoreFileTrackerFactory.create(UTIL.getConfiguration(), false, storeContext); 521 references = references || sft.hasReferences(); 522 if (references) { 523 break; 524 } 525 } 526 return !references; 527 } catch (IOException e) { 528 LOG.warn("Failed check merged region has no reference", e); 529 return false; 530 } 531 }); 532 // run catalog janitor to clean and wait for parent regions are archived 533 UTIL.getMiniHBaseCluster().getMaster().getCatalogJanitor().choreForTesting(); 534 UTIL.waitFor(timeout, () -> { 535 try { 536 Path tableDir = CommonFSUtils.getTableDir(rootDir, tableName); 537 for (FileStatus fileStatus : fs.listStatus(tableDir)) { 538 String name = fileStatus.getPath().getName(); 539 if (name.equals(region0.getEncodedName()) || name.equals(region1.getEncodedName())) { 540 return false; 541 } 542 } 543 return true; 544 } catch (IOException e) { 545 LOG.warn("Check if parent regions are archived error", e); 546 return false; 547 } 548 }); 549 // set file modify time and then run cleaner 550 long time = EnvironmentEdgeManager.currentTime() - TimeToLiveHFileCleaner.DEFAULT_TTL * 1000; 551 traverseAndSetFileTime(HFileArchiveUtil.getArchivePath(conf), time); 552 UTIL.getMiniHBaseCluster().getMaster().getHFileCleaner().triggerCleanerNow().get(); 553 // scan snapshot 554 try (TableSnapshotScanner scanner = 555 new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName, 556 new Scan().withStartRow(bbb).withStopRow(yyy))) { 557 verifyScanner(scanner, bbb, yyy); 558 } 559 } catch (Exception e) { 560 LOG.error("scan snapshot error", e); 561 Assert.fail("Should not throw Exception: " + e.getMessage()); 562 } 563 } 564 565 @Test 566 public void testDeleteTableWithMergedRegions() throws Exception { 567 final TableName tableName = TableName.valueOf(this.name.getMethodName()); 568 String snapshotName = tableName.getNameAsString() + "_snapshot"; 569 Configuration conf = UTIL.getConfiguration(); 570 try (Admin admin = UTIL.getConnection().getAdmin()) { 571 // disable compaction 572 admin.compactionSwitch(false, 573 admin.getRegionServers().stream().map(s -> s.getServerName()).collect(Collectors.toList())); 574 // create table 575 Table table = UTIL.createTable(tableName, FAMILIES, 1, bbb, yyy, 3); 576 List<RegionInfo> regions = admin.getRegions(tableName); 577 Assert.assertEquals(3, regions.size()); 578 // write some data 579 UTIL.loadTable(table, FAMILIES); 580 // merge region 581 admin.mergeRegionsAsync(new byte[][] { regions.get(0).getEncodedNameAsBytes(), 582 regions.get(1).getEncodedNameAsBytes() }, false).get(); 583 regions = admin.getRegions(tableName); 584 Assert.assertEquals(2, regions.size()); 585 // snapshot 586 admin.snapshot(snapshotName, tableName); 587 // verify snapshot 588 try (TableSnapshotScanner scanner = 589 new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName, 590 new Scan().withStartRow(bbb).withStopRow(yyy))) { 591 verifyScanner(scanner, bbb, yyy); 592 } 593 // drop table 594 admin.disableTable(tableName); 595 admin.deleteTable(tableName); 596 // verify snapshot 597 try (TableSnapshotScanner scanner = 598 new TableSnapshotScanner(conf, UTIL.getDataTestDirOnTestFS(snapshotName), snapshotName, 599 new Scan().withStartRow(bbb).withStopRow(yyy))) { 600 verifyScanner(scanner, bbb, yyy); 601 } 602 } 603 } 604 605 private void traverseAndSetFileTime(Path path, long time) throws IOException { 606 fs.setTimes(path, time, -1); 607 if (fs.isDirectory(path)) { 608 List<FileStatus> allPaths = Arrays.asList(fs.listStatus(path)); 609 List<FileStatus> subDirs = 610 allPaths.stream().filter(FileStatus::isDirectory).collect(Collectors.toList()); 611 List<FileStatus> files = 612 allPaths.stream().filter(FileStatus::isFile).collect(Collectors.toList()); 613 for (FileStatus subDir : subDirs) { 614 traverseAndSetFileTime(subDir.getPath(), time); 615 } 616 for (FileStatus file : files) { 617 fs.setTimes(file.getPath(), time, -1); 618 } 619 } 620 } 621}