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