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