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.snapshot; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertFalse; 022import static org.junit.jupiter.api.Assertions.assertTrue; 023import static org.junit.jupiter.api.Assertions.fail; 024 025import java.io.IOException; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.HashSet; 029import java.util.List; 030import java.util.Map; 031import java.util.Set; 032import java.util.TreeSet; 033import org.apache.hadoop.conf.Configuration; 034import org.apache.hadoop.fs.FSDataInputStream; 035import org.apache.hadoop.fs.FSDataOutputStream; 036import org.apache.hadoop.fs.FileStatus; 037import org.apache.hadoop.fs.FileSystem; 038import org.apache.hadoop.fs.Path; 039import org.apache.hadoop.fs.PathFilter; 040import org.apache.hadoop.hbase.HBaseTestingUtil; 041import org.apache.hadoop.hbase.HConstants; 042import org.apache.hadoop.hbase.TableName; 043import org.apache.hadoop.hbase.TableNotEnabledException; 044import org.apache.hadoop.hbase.client.Admin; 045import org.apache.hadoop.hbase.client.BufferedMutator; 046import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 047import org.apache.hadoop.hbase.client.Durability; 048import org.apache.hadoop.hbase.client.Put; 049import org.apache.hadoop.hbase.client.RegionInfo; 050import org.apache.hadoop.hbase.client.RegionInfoBuilder; 051import org.apache.hadoop.hbase.client.RegionReplicaUtil; 052import org.apache.hadoop.hbase.client.SnapshotDescription; 053import org.apache.hadoop.hbase.client.SnapshotType; 054import org.apache.hadoop.hbase.client.Table; 055import org.apache.hadoop.hbase.client.TableDescriptor; 056import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 057import org.apache.hadoop.hbase.errorhandling.ForeignExceptionDispatcher; 058import org.apache.hadoop.hbase.io.HFileLink; 059import org.apache.hadoop.hbase.master.HMaster; 060import org.apache.hadoop.hbase.master.MasterFileSystem; 061import org.apache.hadoop.hbase.mob.MobUtils; 062import org.apache.hadoop.hbase.regionserver.HRegion; 063import org.apache.hadoop.hbase.regionserver.HRegionFileSystem; 064import org.apache.hadoop.hbase.regionserver.HRegionServer; 065import org.apache.hadoop.hbase.util.Bytes; 066import org.apache.hadoop.hbase.util.CommonFSUtils; 067import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 068import org.apache.hadoop.hbase.util.FSTableDescriptors; 069import org.apache.hadoop.hbase.util.FSVisitor; 070import org.apache.hadoop.hbase.util.MD5Hash; 071import org.apache.yetus.audience.InterfaceAudience; 072import org.slf4j.Logger; 073import org.slf4j.LoggerFactory; 074 075import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 076import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.IsSnapshotDoneRequest; 077import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos; 078import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotRegionManifest; 079 080/** 081 * Utilities class for snapshots 082 */ 083@InterfaceAudience.Private 084public final class SnapshotTestingUtils { 085 private static final Logger LOG = LoggerFactory.getLogger(SnapshotTestingUtils.class); 086 087 // default number of regions (and keys) given by getSplitKeys() and createTable() 088 private static byte[] KEYS = Bytes.toBytes("0123456"); 089 090 private SnapshotTestingUtils() { 091 // private constructor for utility class 092 } 093 094 /** 095 * Assert that we don't have any snapshots lists if the admin operation fails 096 */ 097 public static void assertNoSnapshots(Admin admin) throws IOException { 098 assertEquals(0, admin.listSnapshots().size(), "Have some previous snapshots"); 099 } 100 101 /** 102 * Make sure that there is only one snapshot returned from the master and its name and table match 103 * the passed in parameters. 104 */ 105 public static List<SnapshotDescription> assertExistsMatchingSnapshot(Admin admin, 106 String snapshotName, TableName tableName) throws IOException { 107 // list the snapshot 108 List<SnapshotDescription> snapshots = admin.listSnapshots(); 109 110 List<SnapshotDescription> returnedSnapshots = new ArrayList<>(); 111 for (SnapshotDescription sd : snapshots) { 112 if (snapshotName.equals(sd.getName()) && tableName.equals(sd.getTableName())) { 113 returnedSnapshots.add(sd); 114 } 115 } 116 117 assertTrue(returnedSnapshots.size() > 0, "No matching snapshots found."); 118 return returnedSnapshots; 119 } 120 121 /** 122 * Make sure that there is only one snapshot returned from the master 123 */ 124 public static void assertOneSnapshotThatMatches(Admin admin, 125 SnapshotProtos.SnapshotDescription snapshot) throws IOException { 126 assertOneSnapshotThatMatches(admin, snapshot.getName(), TableName.valueOf(snapshot.getTable())); 127 } 128 129 /** 130 * Make sure that there is only one snapshot returned from the master and its name and table match 131 * the passed in parameters. 132 */ 133 public static List<SnapshotDescription> assertOneSnapshotThatMatches(Admin admin, 134 String snapshotName, TableName tableName) throws IOException { 135 // list the snapshot 136 List<SnapshotDescription> snapshots = admin.listSnapshots(); 137 138 assertEquals(1, snapshots.size(), "Should only have 1 snapshot"); 139 assertEquals(snapshotName, snapshots.get(0).getName()); 140 assertEquals(tableName, snapshots.get(0).getTableName()); 141 142 return snapshots; 143 } 144 145 /** 146 * Make sure that there is only one snapshot returned from the master and its name and table match 147 * the passed in parameters. 148 */ 149 public static List<SnapshotDescription> assertOneSnapshotThatMatches(Admin admin, byte[] snapshot, 150 TableName tableName) throws IOException { 151 return assertOneSnapshotThatMatches(admin, Bytes.toString(snapshot), tableName); 152 } 153 154 public static void confirmSnapshotValid(HBaseTestingUtil testUtil, 155 SnapshotProtos.SnapshotDescription snapshotDescriptor, TableName tableName, byte[] family) 156 throws IOException { 157 MasterFileSystem mfs = testUtil.getHBaseCluster().getMaster().getMasterFileSystem(); 158 confirmSnapshotValid(snapshotDescriptor, tableName, family, mfs.getRootDir(), 159 testUtil.getAdmin(), mfs.getFileSystem()); 160 } 161 162 /** 163 * Confirm that the snapshot contains references to all the files that should be in the snapshot. 164 */ 165 public static void confirmSnapshotValid(SnapshotProtos.SnapshotDescription snapshotDescriptor, 166 TableName tableName, byte[] testFamily, Path rootDir, Admin admin, FileSystem fs) 167 throws IOException { 168 ArrayList nonEmptyTestFamilies = new ArrayList(1); 169 nonEmptyTestFamilies.add(testFamily); 170 confirmSnapshotValid(snapshotDescriptor, tableName, nonEmptyTestFamilies, null, rootDir, admin, 171 fs); 172 } 173 174 /** 175 * Confirm that the snapshot has no references files but only metadata. 176 */ 177 public static void confirmEmptySnapshotValid( 178 SnapshotProtos.SnapshotDescription snapshotDescriptor, TableName tableName, byte[] testFamily, 179 Path rootDir, Admin admin, FileSystem fs) throws IOException { 180 ArrayList emptyTestFamilies = new ArrayList(1); 181 emptyTestFamilies.add(testFamily); 182 confirmSnapshotValid(snapshotDescriptor, tableName, null, emptyTestFamilies, rootDir, admin, 183 fs); 184 } 185 186 /** 187 * Confirm that the snapshot contains references to all the files that should be in the snapshot. 188 * This method also perform some redundant check like the existence of the snapshotinfo or the 189 * regioninfo which are done always by the MasterSnapshotVerifier, at the end of the snapshot 190 * operation. 191 */ 192 public static void confirmSnapshotValid(SnapshotProtos.SnapshotDescription snapshotDescriptor, 193 TableName tableName, List<byte[]> nonEmptyTestFamilies, List<byte[]> emptyTestFamilies, 194 Path rootDir, Admin admin, FileSystem fs) throws IOException { 195 final Configuration conf = admin.getConfiguration(); 196 197 // check snapshot dir 198 Path snapshotDir = 199 SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotDescriptor, rootDir); 200 assertTrue(fs.exists(snapshotDir), 201 "target snapshot directory, '" + snapshotDir + "', doesn't exist."); 202 203 SnapshotProtos.SnapshotDescription desc = 204 SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir); 205 206 // Extract regions and families with store files 207 final Set<byte[]> snapshotFamilies = new TreeSet<>(Bytes.BYTES_COMPARATOR); 208 209 SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, desc); 210 Map<String, SnapshotRegionManifest> regionManifests = manifest.getRegionManifestsMap(); 211 for (SnapshotRegionManifest regionManifest : regionManifests.values()) { 212 SnapshotReferenceUtil.visitRegionStoreFiles(regionManifest, 213 new SnapshotReferenceUtil.StoreFileVisitor() { 214 @Override 215 public void storeFile(final RegionInfo regionInfo, final String family, 216 final SnapshotRegionManifest.StoreFile storeFile) throws IOException { 217 snapshotFamilies.add(Bytes.toBytes(family)); 218 } 219 }); 220 } 221 // Verify that there are store files in the specified families 222 if (nonEmptyTestFamilies != null) { 223 for (final byte[] familyName : nonEmptyTestFamilies) { 224 assertTrue(snapshotFamilies.contains(familyName), "Expected snapshot to contain family '" 225 + Bytes.toString(familyName) + "', but it does not."); 226 } 227 } 228 229 // Verify that there are no store files in the specified families 230 if (emptyTestFamilies != null) { 231 for (final byte[] familyName : emptyTestFamilies) { 232 assertFalse(snapshotFamilies.contains(familyName), 233 "Expected snapshot to skip empty family '" + Bytes.toString(familyName) 234 + "', but it is present."); 235 } 236 } 237 238 // check the region snapshot for all the regions 239 List<RegionInfo> regions = admin.getRegions(tableName); 240 // remove the non-default regions 241 RegionReplicaUtil.removeNonDefaultRegions(regions); 242 boolean hasMob = 243 regionManifests.containsKey(MobUtils.getMobRegionInfo(tableName).getEncodedName()); 244 if (hasMob) { 245 assertEquals(regions.size(), regionManifests.size() - 1, "Wrong number of regions."); 246 } else { 247 // if create snapshot when table splitting, parent region will be included to the snapshot 248 // region manifest. we should exclude the parent regions. 249 int regionCountExclusiveSplitParent = 0; 250 for (SnapshotRegionManifest snapshotRegionManifest : regionManifests.values()) { 251 RegionInfo hri = ProtobufUtil.toRegionInfo(snapshotRegionManifest.getRegionInfo()); 252 if (hri.isOffline() && (hri.isSplit() || hri.isSplitParent())) { 253 continue; 254 } 255 regionCountExclusiveSplitParent++; 256 } 257 assertEquals(regions.size(), regionCountExclusiveSplitParent, "Wrong number of regions."); 258 } 259 260 // Verify Regions (redundant check, see MasterSnapshotVerifier) 261 for (RegionInfo info : regions) { 262 String regionName = info.getEncodedName(); 263 assertTrue(regionManifests.containsKey(regionName), 264 "Missing region name: '" + regionName + "'"); 265 } 266 } 267 268 /* 269 * Take snapshot with maximum of numTries attempts, ignoring CorruptedSnapshotException except for 270 * the last CorruptedSnapshotException 271 */ 272 public static void snapshot(Admin admin, final String snapshotName, final TableName tableName, 273 final SnapshotType type, final int numTries) throws IOException { 274 snapshot(admin, snapshotName, tableName, type, numTries, null); 275 } 276 277 /* 278 * Take snapshot having snapshot properties with maximum of numTries attempts, ignoring 279 * CorruptedSnapshotException except for the last CorruptedSnapshotException 280 */ 281 public static void snapshot(Admin admin, final String snapshotName, final TableName tableName, 282 final SnapshotType type, final int numTries, Map<String, Object> snapshotProps) 283 throws IOException { 284 int tries = 0; 285 CorruptedSnapshotException lastEx = null; 286 while (tries++ < numTries) { 287 try { 288 admin.snapshot(snapshotName, tableName, type, snapshotProps); 289 return; 290 } catch (CorruptedSnapshotException cse) { 291 LOG.warn("Got CorruptedSnapshotException", cse); 292 lastEx = cse; 293 } 294 } 295 throw lastEx; 296 } 297 298 public static void cleanupSnapshot(Admin admin, byte[] tableName) throws IOException { 299 SnapshotTestingUtils.cleanupSnapshot(admin, Bytes.toString(tableName)); 300 } 301 302 public static void cleanupSnapshot(Admin admin, String snapshotName) throws IOException { 303 // delete the taken snapshot 304 admin.deleteSnapshot(snapshotName); 305 assertNoSnapshots(admin); 306 } 307 308 /** 309 * Expect the snapshot to throw an error when checking if the snapshot is complete 310 * @param master master to check 311 * @param snapshot the {@link SnapshotDescription} request to pass to the master 312 * @param clazz expected exception from the master 313 */ 314 public static void expectSnapshotDoneException(HMaster master, IsSnapshotDoneRequest snapshot, 315 Class<? extends HBaseSnapshotException> clazz) { 316 try { 317 master.getMasterRpcServices().isSnapshotDone(null, snapshot); 318 fail("didn't fail to lookup a snapshot"); 319 } catch (org.apache.hbase.thirdparty.com.google.protobuf.ServiceException se) { 320 try { 321 throw ProtobufUtil.handleRemoteException(se); 322 } catch (HBaseSnapshotException e) { 323 assertEquals(clazz, e.getClass(), "Threw wrong snapshot exception!"); 324 } catch (Throwable t) { 325 fail("Threw an unexpected exception:" + t); 326 } 327 } 328 } 329 330 /** 331 * List all the HFiles in the given table 332 * @param fs FileSystem where the table lives 333 * @param tableDir directory of the table 334 * @return array of the current HFiles in the table (could be a zero-length array) 335 * @throws IOException on unexecpted error reading the FS 336 */ 337 public static ArrayList<String> listHFileNames(final FileSystem fs, final Path tableDir) 338 throws IOException { 339 final ArrayList<String> hfiles = new ArrayList<>(); 340 FSVisitor.visitTableStoreFiles(fs, tableDir, new FSVisitor.StoreFileVisitor() { 341 @Override 342 public void storeFile(final String region, final String family, final String hfileName) 343 throws IOException { 344 hfiles.add(hfileName); 345 } 346 }); 347 Collections.sort(hfiles); 348 return hfiles; 349 } 350 351 /** 352 * Take a snapshot of the specified table and verify that the given family is not empty. Note that 353 * this will leave the table disabled in the case of an offline snapshot. 354 */ 355 public static void createSnapshotAndValidate(Admin admin, TableName tableName, String familyName, 356 String snapshotNameString, Path rootDir, FileSystem fs, boolean onlineSnapshot) 357 throws Exception { 358 ArrayList<byte[]> nonEmptyFamilyNames = new ArrayList<>(1); 359 nonEmptyFamilyNames.add(Bytes.toBytes(familyName)); 360 createSnapshotAndValidate(admin, tableName, nonEmptyFamilyNames, /* emptyFamilyNames= */ null, 361 snapshotNameString, rootDir, fs, onlineSnapshot); 362 } 363 364 /** 365 * Take a snapshot of the specified table and verify the given families. Note that this will leave 366 * the table disabled in the case of an offline snapshot. 367 */ 368 public static void createSnapshotAndValidate(Admin admin, TableName tableName, 369 List<byte[]> nonEmptyFamilyNames, List<byte[]> emptyFamilyNames, String snapshotNameString, 370 Path rootDir, FileSystem fs, boolean onlineSnapshot) throws Exception { 371 if (!onlineSnapshot) { 372 try { 373 LOG.info("prepping for offline snapshot."); 374 admin.disableTable(tableName); 375 } catch (TableNotEnabledException tne) { 376 LOG.info("In attempting to disable " + tableName + " it turns out that the this table is " 377 + "already disabled."); 378 } 379 } 380 LOG.info("taking snapshot."); 381 admin.snapshot(snapshotNameString, tableName); 382 383 LOG.info("Confirming snapshot exists."); 384 List<SnapshotDescription> snapshots = 385 SnapshotTestingUtils.assertExistsMatchingSnapshot(admin, snapshotNameString, tableName); 386 if (snapshots == null || snapshots.size() != 1) { 387 fail("Incorrect number of snapshots for table " + tableName); 388 } 389 390 LOG.info("validating snapshot."); 391 SnapshotTestingUtils.confirmSnapshotValid( 392 ProtobufUtil.createHBaseProtosSnapshotDesc(snapshots.get(0)), tableName, nonEmptyFamilyNames, 393 emptyFamilyNames, rootDir, admin, fs); 394 } 395 396 /** 397 * Corrupt the specified snapshot by deleting some files. 398 * @param util {@link HBaseTestingUtil} 399 * @param snapshotName name of the snapshot to corrupt 400 * @return array of the corrupted HFiles 401 * @throws IOException on unexecpted error reading the FS 402 */ 403 public static ArrayList corruptSnapshot(final HBaseTestingUtil util, final String snapshotName) 404 throws IOException { 405 final MasterFileSystem mfs = util.getHBaseCluster().getMaster().getMasterFileSystem(); 406 final FileSystem fs = mfs.getFileSystem(); 407 408 Path snapshotDir = 409 SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, mfs.getRootDir()); 410 SnapshotProtos.SnapshotDescription snapshotDesc = 411 SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir); 412 final TableName table = TableName.valueOf(snapshotDesc.getTable()); 413 414 final ArrayList corruptedFiles = new ArrayList(); 415 final Configuration conf = util.getConfiguration(); 416 SnapshotReferenceUtil.visitTableStoreFiles(conf, fs, snapshotDir, snapshotDesc, 417 new SnapshotReferenceUtil.StoreFileVisitor() { 418 @Override 419 public void storeFile(final RegionInfo regionInfo, final String family, 420 final SnapshotRegionManifest.StoreFile storeFile) throws IOException { 421 String region = regionInfo.getEncodedName(); 422 String hfile = storeFile.getName(); 423 HFileLink link = HFileLink.build(conf, table, region, family, hfile); 424 if (corruptedFiles.size() % 2 == 0) { 425 fs.delete(link.getAvailablePath(fs), true); 426 corruptedFiles.add(hfile); 427 } 428 } 429 }); 430 431 assertTrue(corruptedFiles.size() > 0); 432 return corruptedFiles; 433 } 434 435 // ========================================================================== 436 // Snapshot Mock 437 // ========================================================================== 438 public static class SnapshotMock { 439 protected final static String TEST_FAMILY = "cf"; 440 public final static int TEST_NUM_REGIONS = 4; 441 442 private final Configuration conf; 443 private final FileSystem fs; 444 private final Path rootDir; 445 446 static class RegionData { 447 public RegionInfo hri; 448 public Path tableDir; 449 public Path[] files; 450 451 public RegionData(final Path tableDir, final RegionInfo hri, final int nfiles) { 452 this.tableDir = tableDir; 453 this.hri = hri; 454 this.files = new Path[nfiles]; 455 } 456 } 457 458 public static class SnapshotBuilder { 459 private final RegionData[] tableRegions; 460 private final SnapshotProtos.SnapshotDescription desc; 461 private final TableDescriptor htd; 462 private final Configuration conf; 463 private final FileSystem fs; 464 private final Path rootDir; 465 private Path snapshotDir; 466 private int snapshotted = 0; 467 468 public SnapshotBuilder(final Configuration conf, final FileSystem fs, final Path rootDir, 469 final TableDescriptor htd, final SnapshotProtos.SnapshotDescription desc, 470 final RegionData[] tableRegions) throws IOException { 471 this.fs = fs; 472 this.conf = conf; 473 this.rootDir = rootDir; 474 this.htd = htd; 475 this.desc = desc; 476 this.tableRegions = tableRegions; 477 this.snapshotDir = SnapshotDescriptionUtils.getWorkingSnapshotDir(desc, rootDir, conf); 478 FSTableDescriptors.createTableDescriptorForTableDirectory( 479 this.snapshotDir.getFileSystem(conf), snapshotDir, htd, false); 480 } 481 482 public TableDescriptor getTableDescriptor() { 483 return this.htd; 484 } 485 486 public SnapshotProtos.SnapshotDescription getSnapshotDescription() { 487 return this.desc; 488 } 489 490 public Path getSnapshotsDir() { 491 return this.snapshotDir; 492 } 493 494 public Path[] addRegion() throws IOException { 495 return addRegion(desc); 496 } 497 498 public Path[] addRegionV1() throws IOException { 499 return addRegion( 500 desc.toBuilder().setVersion(SnapshotManifestV1.DESCRIPTOR_VERSION).build()); 501 } 502 503 public Path[] addRegionV2() throws IOException { 504 return addRegion( 505 desc.toBuilder().setVersion(SnapshotManifestV2.DESCRIPTOR_VERSION).build()); 506 } 507 508 private Path[] addRegion(final SnapshotProtos.SnapshotDescription desc) throws IOException { 509 if (this.snapshotted == tableRegions.length) { 510 throw new UnsupportedOperationException("No more regions in the table"); 511 } 512 513 RegionData regionData = tableRegions[this.snapshotted++]; 514 ForeignExceptionDispatcher monitor = new ForeignExceptionDispatcher(desc.getName()); 515 SnapshotManifest manifest = SnapshotManifest.create(conf, fs, snapshotDir, desc, monitor); 516 manifest.addTableDescriptor(htd); 517 manifest.addRegion(regionData.tableDir, regionData.hri); 518 return regionData.files; 519 } 520 521 private void corruptFile(Path p) throws IOException { 522 String manifestName = p.getName(); 523 524 // Rename the original region-manifest file 525 Path newP = new Path(p.getParent(), manifestName + "1"); 526 fs.rename(p, newP); 527 528 // Create a new region-manifest file 529 FSDataOutputStream out = fs.create(p); 530 531 // Copy the first 25 bytes of the original region-manifest into the new one, 532 // make it a corrupted region-manifest file. 533 FSDataInputStream input = fs.open(newP); 534 byte[] buffer = new byte[25]; 535 int len = input.read(0, buffer, 0, 25); 536 if (len > 1) { 537 out.write(buffer, 0, len - 1); 538 } 539 out.close(); 540 541 // Delete the original region-manifest 542 fs.delete(newP); 543 } 544 545 /** 546 * Corrupt one region-manifest file 547 * @throws IOException on unexecpted error from the FS 548 */ 549 public void corruptOneRegionManifest() throws IOException { 550 FileStatus[] manifestFiles = CommonFSUtils.listStatus(fs, snapshotDir, new PathFilter() { 551 @Override 552 public boolean accept(Path path) { 553 return path.getName().startsWith(SnapshotManifestV2.SNAPSHOT_MANIFEST_PREFIX); 554 } 555 }); 556 557 if (manifestFiles.length == 0) return; 558 559 // Just choose the first one 560 Path p = manifestFiles[0].getPath(); 561 corruptFile(p); 562 } 563 564 public void missOneRegionSnapshotFile() throws IOException { 565 FileStatus[] manifestFiles = CommonFSUtils.listStatus(fs, snapshotDir); 566 for (FileStatus fileStatus : manifestFiles) { 567 String fileName = fileStatus.getPath().getName(); 568 if ( 569 fileName.endsWith(SnapshotDescriptionUtils.SNAPSHOTINFO_FILE) 570 || fileName.endsWith(".tabledesc") 571 || fileName.endsWith(SnapshotDescriptionUtils.SNAPSHOT_TMP_DIR_NAME) 572 ) { 573 fs.delete(fileStatus.getPath(), true); 574 } 575 } 576 } 577 578 /** 579 * Corrupt data-manifest file 580 * @throws IOException on unexecpted error from the FS 581 */ 582 public void corruptDataManifest() throws IOException { 583 FileStatus[] manifestFiles = CommonFSUtils.listStatus(fs, snapshotDir, new PathFilter() { 584 @Override 585 public boolean accept(Path path) { 586 return path.getName().startsWith(SnapshotManifest.DATA_MANIFEST_NAME); 587 } 588 }); 589 590 if (manifestFiles.length == 0) return; 591 592 // Just choose the first one 593 Path p = manifestFiles[0].getPath(); 594 corruptFile(p); 595 } 596 597 public Path commit() throws IOException { 598 ForeignExceptionDispatcher monitor = new ForeignExceptionDispatcher(desc.getName()); 599 SnapshotManifest manifest = SnapshotManifest.create(conf, fs, snapshotDir, desc, monitor); 600 manifest.addTableDescriptor(htd); 601 manifest.consolidate(); 602 Path finishedDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(desc, rootDir); 603 SnapshotDescriptionUtils.completeSnapshot(finishedDir, snapshotDir, fs, 604 snapshotDir.getFileSystem(conf), conf); 605 snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(desc, rootDir); 606 return snapshotDir; 607 } 608 609 public void consolidate() throws IOException { 610 ForeignExceptionDispatcher monitor = new ForeignExceptionDispatcher(desc.getName()); 611 SnapshotManifest manifest = SnapshotManifest.create(conf, fs, snapshotDir, desc, monitor); 612 manifest.addTableDescriptor(htd); 613 manifest.consolidate(); 614 } 615 } 616 617 public SnapshotMock(final Configuration conf, final FileSystem fs, final Path rootDir) { 618 this.fs = fs; 619 this.conf = conf; 620 this.rootDir = rootDir; 621 } 622 623 public SnapshotBuilder createSnapshotV1(final String snapshotName, final String tableName) 624 throws IOException { 625 return createSnapshot(snapshotName, tableName, SnapshotManifestV1.DESCRIPTOR_VERSION); 626 } 627 628 public SnapshotBuilder createSnapshotV1(final String snapshotName, final String tableName, 629 final int numRegions) throws IOException { 630 return createSnapshot(snapshotName, tableName, numRegions, 631 SnapshotManifestV1.DESCRIPTOR_VERSION); 632 } 633 634 public SnapshotBuilder createSnapshotV2(final String snapshotName, final String tableName) 635 throws IOException { 636 return createSnapshot(snapshotName, tableName, SnapshotManifestV2.DESCRIPTOR_VERSION); 637 } 638 639 public SnapshotBuilder createSnapshotV2(final String snapshotName, final String tableName, 640 final int numRegions) throws IOException { 641 return createSnapshot(snapshotName, tableName, numRegions, 642 SnapshotManifestV2.DESCRIPTOR_VERSION); 643 } 644 645 public SnapshotBuilder createSnapshotV2(final String snapshotName, final String tableName, 646 final int numRegions, final long ttl) throws IOException { 647 return createSnapshot(snapshotName, tableName, numRegions, 648 SnapshotManifestV2.DESCRIPTOR_VERSION, ttl); 649 } 650 651 private SnapshotBuilder createSnapshot(final String snapshotName, final String tableName, 652 final int version) throws IOException { 653 return createSnapshot(snapshotName, tableName, TEST_NUM_REGIONS, version); 654 } 655 656 private SnapshotBuilder createSnapshot(final String snapshotName, final String tableName, 657 final int numRegions, final int version) throws IOException { 658 TableDescriptor htd = createHtd(tableName); 659 RegionData[] regions = createTable(htd, numRegions); 660 661 SnapshotProtos.SnapshotDescription desc = SnapshotProtos.SnapshotDescription.newBuilder() 662 .setTable(htd.getTableName().getNameAsString()).setName(snapshotName).setVersion(version) 663 .build(); 664 665 Path workingDir = SnapshotDescriptionUtils.getWorkingSnapshotDir(desc, rootDir, conf); 666 FileSystem workingFs = workingDir.getFileSystem(conf); 667 SnapshotDescriptionUtils.writeSnapshotInfo(desc, workingDir, workingFs); 668 return new SnapshotBuilder(conf, fs, rootDir, htd, desc, regions); 669 } 670 671 private SnapshotBuilder createSnapshot(final String snapshotName, final String tableName, 672 final int numRegions, final int version, final long ttl) throws IOException { 673 TableDescriptor htd = createHtd(tableName); 674 RegionData[] regions = createTable(htd, numRegions); 675 SnapshotProtos.SnapshotDescription desc = SnapshotProtos.SnapshotDescription.newBuilder() 676 .setTable(htd.getTableName().getNameAsString()).setName(snapshotName).setVersion(version) 677 .setCreationTime(EnvironmentEdgeManager.currentTime()).setTtl(ttl).build(); 678 Path workingDir = SnapshotDescriptionUtils.getWorkingSnapshotDir(desc, rootDir, conf); 679 SnapshotDescriptionUtils.writeSnapshotInfo(desc, workingDir, fs); 680 return new SnapshotBuilder(conf, fs, rootDir, htd, desc, regions); 681 } 682 683 public TableDescriptor createHtd(final String tableName) { 684 return TableDescriptorBuilder.newBuilder(TableName.valueOf(tableName)) 685 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(TEST_FAMILY)).build(); 686 } 687 688 private RegionData[] createTable(final TableDescriptor htd, final int nregions) 689 throws IOException { 690 Path tableDir = CommonFSUtils.getTableDir(rootDir, htd.getTableName()); 691 new FSTableDescriptors(conf).createTableDescriptorForTableDirectory(tableDir, htd, false); 692 693 assertTrue(nregions % 2 == 0); 694 RegionData[] regions = new RegionData[nregions]; 695 for (int i = 0; i < regions.length; i += 2) { 696 byte[] startKey = Bytes.toBytes(0 + i * 2); 697 byte[] endKey = Bytes.toBytes(1 + i * 2); 698 699 // First region, simple with one plain hfile. 700 RegionInfo hri = RegionInfoBuilder.newBuilder(htd.getTableName()).setStartKey(startKey) 701 .setEndKey(endKey).build(); 702 HRegionFileSystem rfs = HRegionFileSystem.createRegionOnFileSystem(conf, fs, tableDir, hri); 703 regions[i] = new RegionData(tableDir, hri, 3); 704 for (int j = 0; j < regions[i].files.length; ++j) { 705 Path storeFile = createStoreFile(rfs.createTempName()); 706 regions[i].files[j] = rfs.commitStoreFile(TEST_FAMILY, storeFile); 707 } 708 709 // Second region, used to test the split case. 710 // This region contains a reference to the hfile in the first region. 711 startKey = Bytes.toBytes(2 + i * 2); 712 endKey = Bytes.toBytes(3 + i * 2); 713 hri = RegionInfoBuilder.newBuilder(htd.getTableName()).build(); 714 rfs = HRegionFileSystem.createRegionOnFileSystem(conf, fs, tableDir, hri); 715 regions[i + 1] = new RegionData(tableDir, hri, regions[i].files.length); 716 for (int j = 0; j < regions[i].files.length; ++j) { 717 String refName = regions[i].files[j].getName() + '.' + regions[i].hri.getEncodedName(); 718 Path refFile = createStoreFile(new Path(rootDir, refName)); 719 regions[i + 1].files[j] = rfs.commitStoreFile(TEST_FAMILY, refFile); 720 } 721 } 722 return regions; 723 } 724 725 private Path createStoreFile(final Path storeFile) throws IOException { 726 FSDataOutputStream out = fs.create(storeFile); 727 try { 728 out.write(Bytes.toBytes(storeFile.toString())); 729 } finally { 730 out.close(); 731 } 732 return storeFile; 733 } 734 } 735 736 // ========================================================================== 737 // Table Helpers 738 // ========================================================================== 739 public static void waitForTableToBeOnline(final HBaseTestingUtil util, final TableName tableName) 740 throws IOException, InterruptedException { 741 HRegionServer rs = util.getRSForFirstRegionInTable(tableName); 742 List<HRegion> onlineRegions = rs.getRegions(tableName); 743 for (HRegion region : onlineRegions) { 744 region.waitForFlushesAndCompactions(); 745 } 746 // Wait up to 60 seconds for a table to be available. 747 util.waitFor(60000, util.predicateTableAvailable(tableName)); 748 } 749 750 public static void createTable(final HBaseTestingUtil util, final TableName tableName, 751 int regionReplication, int nRegions, final byte[]... families) 752 throws IOException, InterruptedException { 753 TableDescriptorBuilder builder = 754 TableDescriptorBuilder.newBuilder(tableName).setRegionReplication(regionReplication); 755 for (byte[] family : families) { 756 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)); 757 } 758 byte[][] splitKeys = getSplitKeys(nRegions); 759 util.createTable(builder.build(), splitKeys); 760 assertEquals((splitKeys.length + 1) * regionReplication, 761 util.getAdmin().getRegions(tableName).size()); 762 } 763 764 public static byte[][] getSplitKeys() { 765 return getSplitKeys(KEYS.length); 766 } 767 768 public static byte[][] getSplitKeys(int nRegions) { 769 nRegions = nRegions < KEYS.length ? nRegions : (KEYS.length - 1); 770 final byte[][] splitKeys = new byte[nRegions - 1][]; 771 final int step = KEYS.length / nRegions; 772 int keyIndex = 1; 773 for (int i = 0; i < splitKeys.length; ++i) { 774 splitKeys[i] = new byte[] { KEYS[keyIndex] }; 775 keyIndex += step; 776 } 777 return splitKeys; 778 } 779 780 public static void createTable(final HBaseTestingUtil util, final TableName tableName, 781 final byte[]... families) throws IOException, InterruptedException { 782 createTable(util, tableName, 1, families); 783 } 784 785 public static void createTable(final HBaseTestingUtil util, final TableName tableName, 786 final int regionReplication, final byte[]... families) 787 throws IOException, InterruptedException { 788 createTable(util, tableName, regionReplication, KEYS.length, families); 789 } 790 791 public static void createPreSplitTable(final HBaseTestingUtil util, final TableName tableName, 792 final int nRegions, final byte[]... families) throws IOException, InterruptedException { 793 createTable(util, tableName, 1, nRegions, families); 794 } 795 796 public static void loadData(final HBaseTestingUtil util, final TableName tableName, int rows, 797 byte[]... families) throws IOException, InterruptedException { 798 BufferedMutator mutator = util.getConnection().getBufferedMutator(tableName); 799 loadData(util, mutator, rows, families); 800 } 801 802 public static void loadData(final HBaseTestingUtil util, final BufferedMutator mutator, int rows, 803 byte[]... families) throws IOException, InterruptedException { 804 // Ensure one row per region 805 assertTrue(rows >= KEYS.length); 806 for (byte k0 : KEYS) { 807 byte[] k = new byte[] { k0 }; 808 byte[] value = Bytes.add(Bytes.toBytes(EnvironmentEdgeManager.currentTime()), k); 809 byte[] key = Bytes.add(k, Bytes.toBytes(MD5Hash.getMD5AsHex(value))); 810 final byte[][] families1 = families; 811 final byte[] key1 = key; 812 final byte[] value1 = value; 813 mutator.mutate(createPut(families1, key1, value1)); 814 rows--; 815 } 816 817 // Add other extra rows. more rows, more files 818 while (rows-- > 0) { 819 byte[] value = 820 Bytes.add(Bytes.toBytes(EnvironmentEdgeManager.currentTime()), Bytes.toBytes(rows)); 821 byte[] key = Bytes.toBytes(MD5Hash.getMD5AsHex(value)); 822 final byte[][] families1 = families; 823 final byte[] key1 = key; 824 final byte[] value1 = value; 825 mutator.mutate(createPut(families1, key1, value1)); 826 } 827 mutator.flush(); 828 829 waitForTableToBeOnline(util, mutator.getName()); 830 } 831 832 private static Put createPut(final byte[][] families, final byte[] key, final byte[] value) { 833 byte[] q = Bytes.toBytes("q"); 834 Put put = new Put(key); 835 put.setDurability(Durability.SKIP_WAL); 836 for (byte[] family : families) { 837 put.addColumn(family, q, value); 838 } 839 return put; 840 } 841 842 public static void deleteAllSnapshots(final Admin admin) throws IOException { 843 // Delete all the snapshots 844 for (SnapshotDescription snapshot : admin.listSnapshots()) { 845 admin.deleteSnapshot(snapshot.getName()); 846 } 847 SnapshotTestingUtils.assertNoSnapshots(admin); 848 } 849 850 public static void deleteArchiveDirectory(final HBaseTestingUtil util) throws IOException { 851 // Ensure the archiver to be empty 852 MasterFileSystem mfs = util.getMiniHBaseCluster().getMaster().getMasterFileSystem(); 853 Path archiveDir = new Path(mfs.getRootDir(), HConstants.HFILE_ARCHIVE_DIRECTORY); 854 mfs.getFileSystem().delete(archiveDir, true); 855 } 856 857 public static void verifyRowCount(final HBaseTestingUtil util, final TableName tableName, 858 long expectedRows) throws IOException { 859 Table table = util.getConnection().getTable(tableName); 860 try { 861 assertEquals(expectedRows, util.countRows(table)); 862 } finally { 863 table.close(); 864 } 865 } 866 867 public static void verifyReplicasCameOnline(TableName tableName, Admin admin, 868 int regionReplication) throws IOException { 869 List<RegionInfo> regions = admin.getRegions(tableName); 870 HashSet<RegionInfo> set = new HashSet<>(); 871 for (RegionInfo hri : regions) { 872 set.add(RegionReplicaUtil.getRegionInfoForDefaultReplica(hri)); 873 for (int i = 0; i < regionReplication; i++) { 874 RegionInfo replica = RegionReplicaUtil.getRegionInfoForReplica(hri, i); 875 if (!regions.contains(replica)) { 876 fail(replica + " is not contained in the list of online regions"); 877 } 878 } 879 } 880 assertEquals(getSplitKeys().length + 1, set.size()); 881 } 882}