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.master.cleaner; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertTrue; 022import static org.junit.Assert.fail; 023 024import java.io.IOException; 025import java.util.Collection; 026import java.util.List; 027import java.util.Set; 028import java.util.concurrent.Future; 029import java.util.concurrent.TimeUnit; 030import java.util.regex.Pattern; 031import org.apache.hadoop.conf.Configuration; 032import org.apache.hadoop.fs.FileSystem; 033import org.apache.hadoop.fs.Path; 034import org.apache.hadoop.hbase.HBaseClassTestRule; 035import org.apache.hadoop.hbase.HBaseTestingUtility; 036import org.apache.hadoop.hbase.HConstants; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.Waiter; 039import org.apache.hadoop.hbase.Waiter.ExplainingPredicate; 040import org.apache.hadoop.hbase.client.Admin; 041import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 042import org.apache.hadoop.hbase.client.Put; 043import org.apache.hadoop.hbase.client.SnapshotType; 044import org.apache.hadoop.hbase.client.Table; 045import org.apache.hadoop.hbase.client.TableDescriptor; 046import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 047import org.apache.hadoop.hbase.master.HMaster; 048import org.apache.hadoop.hbase.master.snapshot.DisabledTableSnapshotHandler; 049import org.apache.hadoop.hbase.master.snapshot.SnapshotHFileCleaner; 050import org.apache.hadoop.hbase.master.snapshot.SnapshotManager; 051import org.apache.hadoop.hbase.regionserver.CompactedHFilesDischarger; 052import org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy; 053import org.apache.hadoop.hbase.regionserver.HRegion; 054import org.apache.hadoop.hbase.regionserver.HRegionServer; 055import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils; 056import org.apache.hadoop.hbase.snapshot.SnapshotReferenceUtil; 057import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils; 058import org.apache.hadoop.hbase.snapshot.UnknownSnapshotException; 059import org.apache.hadoop.hbase.testclassification.MasterTests; 060import org.apache.hadoop.hbase.testclassification.MediumTests; 061import org.apache.hadoop.hbase.util.Bytes; 062import org.apache.hadoop.hbase.util.CommonFSUtils; 063import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 064import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; 065import org.junit.After; 066import org.junit.AfterClass; 067import org.junit.Assert; 068import org.junit.Before; 069import org.junit.BeforeClass; 070import org.junit.ClassRule; 071import org.junit.Test; 072import org.junit.experimental.categories.Category; 073import org.mockito.Mockito; 074import org.slf4j.Logger; 075import org.slf4j.LoggerFactory; 076 077import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 078import org.apache.hbase.thirdparty.com.google.common.util.concurrent.Uninterruptibles; 079 080import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.DeleteSnapshotRequest; 081import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetCompletedSnapshotsRequest; 082import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetCompletedSnapshotsResponse; 083import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.IsSnapshotCleanupEnabledRequest; 084import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.IsSnapshotCleanupEnabledResponse; 085import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.IsSnapshotDoneRequest; 086import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.IsSnapshotDoneResponse; 087import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.SetSnapshotCleanupRequest; 088import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotDescription; 089 090/** 091 * Test the master-related aspects of a snapshot 092 */ 093@Category({ MasterTests.class, MediumTests.class }) 094public class TestSnapshotFromMaster { 095 096 @ClassRule 097 public static final HBaseClassTestRule CLASS_RULE = 098 HBaseClassTestRule.forClass(TestSnapshotFromMaster.class); 099 100 private static final Logger LOG = LoggerFactory.getLogger(TestSnapshotFromMaster.class); 101 private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 102 private static final int NUM_RS = 2; 103 private static Path rootDir; 104 private static FileSystem fs; 105 private static HMaster master; 106 107 // for hfile archiving test. 108 private static Path archiveDir; 109 private static final byte[] TEST_FAM = Bytes.toBytes("fam"); 110 private static final TableName TABLE_NAME = TableName.valueOf("test"); 111 // refresh the cache every 1/2 second 112 private static final long cacheRefreshPeriod = 500; 113 private static final int blockingStoreFiles = 12; 114 115 /** 116 * Setup the config for the cluster 117 */ 118 @BeforeClass 119 public static void setupCluster() throws Exception { 120 setupConf(UTIL.getConfiguration()); 121 UTIL.startMiniCluster(NUM_RS); 122 fs = UTIL.getDFSCluster().getFileSystem(); 123 master = UTIL.getMiniHBaseCluster().getMaster(); 124 rootDir = master.getMasterFileSystem().getRootDir(); 125 archiveDir = new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY); 126 } 127 128 private static void setupConf(Configuration conf) { 129 // disable the ui 130 conf.setInt("hbase.regionsever.info.port", -1); 131 // change the flush size to a small amount, regulating number of store files 132 conf.setInt("hbase.hregion.memstore.flush.size", 25000); 133 // so make sure we get a compaction when doing a load, but keep around some 134 // files in the store 135 conf.setInt("hbase.hstore.compaction.min", 2); 136 conf.setInt("hbase.hstore.compactionThreshold", 5); 137 // block writes if we get to 12 store files 138 conf.setInt("hbase.hstore.blockingStoreFiles", blockingStoreFiles); 139 // Ensure no extra cleaners on by default (e.g. TimeToLiveHFileCleaner) 140 conf.set(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS, ""); 141 conf.set(HConstants.HBASE_MASTER_LOGCLEANER_PLUGINS, ""); 142 // Enable snapshot 143 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true); 144 conf.setLong(SnapshotManager.HBASE_SNAPSHOT_SENTINELS_CLEANUP_TIMEOUT_MILLIS, 3 * 1000L); 145 conf.setLong(SnapshotHFileCleaner.HFILE_CACHE_REFRESH_PERIOD_CONF_KEY, cacheRefreshPeriod); 146 conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY, 147 ConstantSizeRegionSplitPolicy.class.getName()); 148 conf.setInt("hbase.hfile.compactions.cleaner.interval", 20 * 1000); 149 conf.setInt("hbase.master.cleaner.snapshot.interval", 500); 150 } 151 152 @Before 153 public void setup() throws Exception { 154 UTIL.createTable(TABLE_NAME, TEST_FAM); 155 master.getSnapshotManager().setSnapshotHandlerForTesting(TABLE_NAME, null); 156 } 157 158 @After 159 public void tearDown() throws Exception { 160 UTIL.deleteTable(TABLE_NAME); 161 SnapshotTestingUtils.deleteAllSnapshots(UTIL.getAdmin()); 162 SnapshotTestingUtils.deleteArchiveDirectory(UTIL); 163 } 164 165 @AfterClass 166 public static void cleanupTest() throws Exception { 167 try { 168 UTIL.shutdownMiniCluster(); 169 } catch (Exception e) { 170 // NOOP; 171 } 172 } 173 174 /** 175 * Test that the contract from the master for checking on a snapshot are valid. 176 * <p> 177 * <ol> 178 * <li>If a snapshot fails with an error, we expect to get the source error.</li> 179 * <li>If there is no snapshot name supplied, we should get an error.</li> 180 * <li>If asking about a snapshot has hasn't occurred, you should get an error.</li> 181 * </ol> 182 */ 183 @Test 184 public void testIsDoneContract() throws Exception { 185 186 IsSnapshotDoneRequest.Builder builder = IsSnapshotDoneRequest.newBuilder(); 187 188 String snapshotName = "asyncExpectedFailureTest"; 189 190 // check that we get an exception when looking up snapshot where one hasn't happened 191 SnapshotTestingUtils.expectSnapshotDoneException(master, builder.build(), 192 UnknownSnapshotException.class); 193 194 // and that we get the same issue, even if we specify a name 195 SnapshotDescription desc = SnapshotDescription.newBuilder().setName(snapshotName) 196 .setTable(TABLE_NAME.getNameAsString()).build(); 197 builder.setSnapshot(desc); 198 SnapshotTestingUtils.expectSnapshotDoneException(master, builder.build(), 199 UnknownSnapshotException.class); 200 201 // set a mock handler to simulate a snapshot 202 DisabledTableSnapshotHandler mockHandler = Mockito.mock(DisabledTableSnapshotHandler.class); 203 Mockito.when(mockHandler.getException()).thenReturn(null); 204 Mockito.when(mockHandler.getSnapshot()).thenReturn(desc); 205 Mockito.when(mockHandler.isFinished()).thenReturn(Boolean.TRUE); 206 Mockito.when(mockHandler.getCompletionTimestamp()) 207 .thenReturn(EnvironmentEdgeManager.currentTime()); 208 209 master.getSnapshotManager().setSnapshotHandlerForTesting(TABLE_NAME, mockHandler); 210 211 // if we do a lookup without a snapshot name, we should fail - you should always know your name 212 builder = IsSnapshotDoneRequest.newBuilder(); 213 SnapshotTestingUtils.expectSnapshotDoneException(master, builder.build(), 214 UnknownSnapshotException.class); 215 216 // then do the lookup for the snapshot that it is done 217 builder.setSnapshot(desc); 218 IsSnapshotDoneResponse response = 219 master.getMasterRpcServices().isSnapshotDone(null, builder.build()); 220 assertTrue("Snapshot didn't complete when it should have.", response.getDone()); 221 222 // now try the case where we are looking for a snapshot we didn't take 223 builder.setSnapshot(SnapshotDescription.newBuilder().setName("Not A Snapshot").build()); 224 SnapshotTestingUtils.expectSnapshotDoneException(master, builder.build(), 225 UnknownSnapshotException.class); 226 227 // then create a snapshot to the fs and make sure that we can find it when checking done 228 snapshotName = "completed"; 229 desc = createSnapshot(snapshotName); 230 231 builder.setSnapshot(desc); 232 response = master.getMasterRpcServices().isSnapshotDone(null, builder.build()); 233 assertTrue("Completed, on-disk snapshot not found", response.getDone()); 234 } 235 236 @Test 237 public void testGetCompletedSnapshots() throws Exception { 238 // first check when there are no snapshots 239 GetCompletedSnapshotsRequest request = GetCompletedSnapshotsRequest.newBuilder().build(); 240 GetCompletedSnapshotsResponse response = 241 master.getMasterRpcServices().getCompletedSnapshots(null, request); 242 assertEquals("Found unexpected number of snapshots", 0, response.getSnapshotsCount()); 243 244 // write one snapshot to the fs 245 String snapshotName = "completed"; 246 SnapshotDescription snapshot = createSnapshot(snapshotName); 247 248 // check that we get one snapshot 249 response = master.getMasterRpcServices().getCompletedSnapshots(null, request); 250 assertEquals("Found unexpected number of snapshots", 1, response.getSnapshotsCount()); 251 List<SnapshotDescription> snapshots = response.getSnapshotsList(); 252 List<SnapshotDescription> expected = Lists.newArrayList(snapshot); 253 assertEquals("Returned snapshots don't match created snapshots", expected, snapshots); 254 255 // write a second snapshot 256 snapshotName = "completed_two"; 257 snapshot = createSnapshot(snapshotName); 258 expected.add(snapshot); 259 260 // check that we get one snapshot 261 response = master.getMasterRpcServices().getCompletedSnapshots(null, request); 262 assertEquals("Found unexpected number of snapshots", 2, response.getSnapshotsCount()); 263 snapshots = response.getSnapshotsList(); 264 assertEquals("Returned snapshots don't match created snapshots", expected, snapshots); 265 } 266 267 @Test 268 public void testDeleteSnapshot() throws Exception { 269 270 String snapshotName = "completed"; 271 SnapshotDescription snapshot = SnapshotDescription.newBuilder().setName(snapshotName).build(); 272 273 DeleteSnapshotRequest request = 274 DeleteSnapshotRequest.newBuilder().setSnapshot(snapshot).build(); 275 try { 276 master.getMasterRpcServices().deleteSnapshot(null, request); 277 fail("Master didn't throw exception when attempting to delete snapshot that doesn't exist"); 278 } catch (org.apache.hbase.thirdparty.com.google.protobuf.ServiceException e) { 279 // Expected 280 } 281 282 // write one snapshot to the fs 283 createSnapshot(snapshotName); 284 285 // then delete the existing snapshot,which shouldn't cause an exception to be thrown 286 master.getMasterRpcServices().deleteSnapshot(null, request); 287 } 288 289 @Test 290 public void testGetCompletedSnapshotsWithCleanup() throws Exception { 291 // Enable auto snapshot cleanup for the cluster 292 SetSnapshotCleanupRequest setSnapshotCleanupRequest = 293 SetSnapshotCleanupRequest.newBuilder().setEnabled(true).build(); 294 master.getMasterRpcServices().switchSnapshotCleanup(null, setSnapshotCleanupRequest); 295 296 // first check when there are no snapshots 297 GetCompletedSnapshotsRequest request = GetCompletedSnapshotsRequest.newBuilder().build(); 298 GetCompletedSnapshotsResponse response = 299 master.getMasterRpcServices().getCompletedSnapshots(null, request); 300 assertEquals("Found unexpected number of snapshots", 0, response.getSnapshotsCount()); 301 302 // NOTE: This is going to be flakey. Its timing based. For now made it more coarse 303 // so more likely to pass though we have to hang around longer. 304 305 // write one snapshot to the fs 306 createSnapshotWithTtl("snapshot_01", 5L); 307 createSnapshotWithTtl("snapshot_02", 100L); 308 309 // check that we get one snapshot 310 response = master.getMasterRpcServices().getCompletedSnapshots(null, request); 311 assertEquals("Found unexpected number of snapshots", 2, response.getSnapshotsCount()); 312 313 // Check that 1 snapshot is auto cleaned after 5 sec of TTL expiration. Wait 10 seconds 314 // just in case. 315 Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS); 316 response = master.getMasterRpcServices().getCompletedSnapshots(null, request); 317 assertEquals("Found unexpected number of snapshots", 1, response.getSnapshotsCount()); 318 } 319 320 @Test 321 public void testGetCompletedSnapshotsWithoutCleanup() throws Exception { 322 // Disable auto snapshot cleanup for the cluster 323 SetSnapshotCleanupRequest setSnapshotCleanupRequest = 324 SetSnapshotCleanupRequest.newBuilder().setEnabled(false).build(); 325 master.getMasterRpcServices().switchSnapshotCleanup(null, setSnapshotCleanupRequest); 326 327 // first check when there are no snapshots 328 GetCompletedSnapshotsRequest request = GetCompletedSnapshotsRequest.newBuilder().build(); 329 GetCompletedSnapshotsResponse response = 330 master.getMasterRpcServices().getCompletedSnapshots(null, request); 331 assertEquals("Found unexpected number of snapshots", 0, response.getSnapshotsCount()); 332 333 // write one snapshot to the fs 334 createSnapshotWithTtl("snapshot_02", 1L); 335 createSnapshotWithTtl("snapshot_03", 1L); 336 337 // check that we get one snapshot 338 response = master.getMasterRpcServices().getCompletedSnapshots(null, request); 339 assertEquals("Found unexpected number of snapshots", 2, response.getSnapshotsCount()); 340 341 // check that no snapshot is auto cleaned even after 1 sec of TTL expiration 342 Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS); 343 response = master.getMasterRpcServices().getCompletedSnapshots(null, request); 344 assertEquals("Found unexpected number of snapshots", 2, response.getSnapshotsCount()); 345 } 346 347 @Test 348 public void testSnapshotCleanupStatus() throws Exception { 349 // Enable auto snapshot cleanup for the cluster 350 SetSnapshotCleanupRequest setSnapshotCleanupRequest = 351 SetSnapshotCleanupRequest.newBuilder().setEnabled(true).build(); 352 master.getMasterRpcServices().switchSnapshotCleanup(null, setSnapshotCleanupRequest); 353 354 // Check if auto snapshot cleanup is enabled 355 IsSnapshotCleanupEnabledRequest isSnapshotCleanupEnabledRequest = 356 IsSnapshotCleanupEnabledRequest.newBuilder().build(); 357 IsSnapshotCleanupEnabledResponse isSnapshotCleanupEnabledResponse = 358 master.getMasterRpcServices().isSnapshotCleanupEnabled(null, isSnapshotCleanupEnabledRequest); 359 Assert.assertTrue(isSnapshotCleanupEnabledResponse.getEnabled()); 360 361 // Disable auto snapshot cleanup for the cluster 362 setSnapshotCleanupRequest = SetSnapshotCleanupRequest.newBuilder().setEnabled(false).build(); 363 master.getMasterRpcServices().switchSnapshotCleanup(null, setSnapshotCleanupRequest); 364 365 // Check if auto snapshot cleanup is disabled 366 isSnapshotCleanupEnabledRequest = IsSnapshotCleanupEnabledRequest.newBuilder().build(); 367 isSnapshotCleanupEnabledResponse = 368 master.getMasterRpcServices().isSnapshotCleanupEnabled(null, isSnapshotCleanupEnabledRequest); 369 Assert.assertFalse(isSnapshotCleanupEnabledResponse.getEnabled()); 370 } 371 372 /** 373 * Test that the snapshot hfile archive cleaner works correctly. HFiles that are in snapshots 374 * should be retained, while those that are not in a snapshot should be deleted. 375 * @throws Exception on failure 376 */ 377 @Test 378 public void testSnapshotHFileArchiving() throws Exception { 379 Admin admin = UTIL.getAdmin(); 380 // make sure we don't fail on listing snapshots 381 SnapshotTestingUtils.assertNoSnapshots(admin); 382 383 // recreate test table with disabled compactions; otherwise compaction may happen before 384 // snapshot, the call after snapshot will be a no-op and checks will fail 385 UTIL.deleteTable(TABLE_NAME); 386 TableDescriptor td = TableDescriptorBuilder.newBuilder(TABLE_NAME) 387 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(TEST_FAM)).setCompactionEnabled(false) 388 .build(); 389 UTIL.getAdmin().createTable(td); 390 391 // load the table 392 for (int i = 0; i < blockingStoreFiles / 2; i++) { 393 UTIL.loadTable(UTIL.getConnection().getTable(TABLE_NAME), TEST_FAM); 394 UTIL.flush(TABLE_NAME); 395 } 396 397 // disable the table so we can take a snapshot 398 admin.disableTable(TABLE_NAME); 399 400 // take a snapshot of the table 401 String snapshotName = "snapshot"; 402 byte[] snapshotNameBytes = Bytes.toBytes(snapshotName); 403 admin.snapshot(snapshotName, TABLE_NAME); 404 405 LOG.info("After snapshot File-System state"); 406 CommonFSUtils.logFileSystemState(fs, rootDir, LOG); 407 408 // ensure we only have one snapshot 409 SnapshotTestingUtils.assertOneSnapshotThatMatches(admin, snapshotNameBytes, TABLE_NAME); 410 411 td = TableDescriptorBuilder.newBuilder(td).setCompactionEnabled(true).build(); 412 // enable compactions now 413 admin.modifyTable(td); 414 415 // renable the table so we can compact the regions 416 admin.enableTable(TABLE_NAME); 417 418 // compact the files so we get some archived files for the table we just snapshotted 419 List<HRegion> regions = UTIL.getHBaseCluster().getRegions(TABLE_NAME); 420 for (HRegion region : regions) { 421 region.waitForFlushesAndCompactions(); // enable can trigger a compaction, wait for it. 422 region.compactStores(); // min is 2 so will compact and archive 423 } 424 List<RegionServerThread> regionServerThreads = 425 UTIL.getMiniHBaseCluster().getRegionServerThreads(); 426 HRegionServer hrs = null; 427 for (RegionServerThread rs : regionServerThreads) { 428 if (!rs.getRegionServer().getRegions(TABLE_NAME).isEmpty()) { 429 hrs = rs.getRegionServer(); 430 break; 431 } 432 } 433 CompactedHFilesDischarger cleaner = new CompactedHFilesDischarger(100, null, hrs, false); 434 cleaner.chore(); 435 LOG.info("After compaction File-System state"); 436 CommonFSUtils.logFileSystemState(fs, rootDir, LOG); 437 438 // make sure the cleaner has run 439 LOG.debug("Running hfile cleaners"); 440 ensureHFileCleanersRun(); 441 LOG.info("After cleaners File-System state: " + rootDir); 442 CommonFSUtils.logFileSystemState(fs, rootDir, LOG); 443 444 // get the snapshot files for the table 445 Path snapshotTable = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir); 446 Set<String> snapshotHFiles = 447 SnapshotReferenceUtil.getHFileNames(UTIL.getConfiguration(), fs, snapshotTable); 448 // check that the files in the archive contain the ones that we need for the snapshot 449 LOG.debug("Have snapshot hfiles:"); 450 for (String fileName : snapshotHFiles) { 451 LOG.debug(fileName); 452 } 453 // get the archived files for the table 454 Collection<String> archives = getHFiles(archiveDir, fs, TABLE_NAME); 455 456 // get the hfiles for the table 457 Collection<String> hfiles = getHFiles(rootDir, fs, TABLE_NAME); 458 459 // and make sure that there is a proper subset 460 for (String fileName : snapshotHFiles) { 461 boolean exist = archives.contains(fileName) || hfiles.contains(fileName); 462 assertTrue("Archived hfiles " + archives + " and table hfiles " + hfiles 463 + " is missing snapshot file:" + fileName, exist); 464 } 465 466 // delete the existing snapshot 467 admin.deleteSnapshot(snapshotNameBytes); 468 SnapshotTestingUtils.assertNoSnapshots(admin); 469 470 // make sure that we don't keep around the hfiles that aren't in a snapshot 471 // make sure we wait long enough to refresh the snapshot hfile 472 List<BaseHFileCleanerDelegate> delegates = 473 UTIL.getMiniHBaseCluster().getMaster().getHFileCleaner().cleanersChain; 474 for (BaseHFileCleanerDelegate delegate : delegates) { 475 if (delegate instanceof SnapshotHFileCleaner) { 476 ((SnapshotHFileCleaner) delegate).getFileCacheForTesting().triggerCacheRefreshForTesting(); 477 } 478 } 479 // run the cleaner again 480 // In SnapshotFileCache, we use lock.tryLock to avoid being blocked by snapshot operation, if we 481 // fail to get the lock, we will not archive any files. This is not a big deal in real world, so 482 // here we will try to run the cleaner multiple times to make the test more stable. 483 // See HBASE-26861 484 UTIL.waitFor(60000, 1000, new ExplainingPredicate<Exception>() { 485 486 @Override 487 public boolean evaluate() throws Exception { 488 LOG.debug("Running hfile cleaners"); 489 ensureHFileCleanersRun(); 490 LOG.info("After delete snapshot cleaners run File-System state"); 491 CommonFSUtils.logFileSystemState(fs, rootDir, LOG); 492 return getHFiles(archiveDir, fs, TABLE_NAME).isEmpty(); 493 } 494 495 @Override 496 public String explainFailure() throws Exception { 497 return "Still have some hfiles in the archive, when their snapshot has been deleted: " 498 + getHFiles(archiveDir, fs, TABLE_NAME); 499 } 500 }); 501 } 502 503 /** 504 * @return all the HFiles for a given table in the specified dir 505 * @throws IOException on expected failure 506 */ 507 private final Collection<String> getHFiles(Path dir, FileSystem fs, TableName tableName) 508 throws IOException { 509 Path tableDir = CommonFSUtils.getTableDir(dir, tableName); 510 return SnapshotTestingUtils.listHFileNames(fs, tableDir); 511 } 512 513 /** 514 * Make sure the {@link HFileCleaner HFileCleaners} run at least once 515 */ 516 private static void ensureHFileCleanersRun() { 517 UTIL.getHBaseCluster().getMaster().getHFileCleaner().chore(); 518 } 519 520 private SnapshotDescription createSnapshot(final String snapshotName) throws IOException { 521 SnapshotTestingUtils.SnapshotMock snapshotMock = 522 new SnapshotTestingUtils.SnapshotMock(UTIL.getConfiguration(), fs, rootDir); 523 SnapshotTestingUtils.SnapshotMock.SnapshotBuilder builder = 524 snapshotMock.createSnapshotV2(snapshotName, "test", 0); 525 builder.commit(); 526 return builder.getSnapshotDescription(); 527 } 528 529 private SnapshotDescription createSnapshotWithTtl(final String snapshotName, final long ttl) 530 throws IOException { 531 SnapshotTestingUtils.SnapshotMock snapshotMock = 532 new SnapshotTestingUtils.SnapshotMock(UTIL.getConfiguration(), fs, rootDir); 533 SnapshotTestingUtils.SnapshotMock.SnapshotBuilder builder = 534 snapshotMock.createSnapshotV2(snapshotName, "test", 0, ttl); 535 builder.commit(); 536 return builder.getSnapshotDescription(); 537 } 538 539 @Test 540 public void testAsyncSnapshotWillNotBlockSnapshotHFileCleaner() throws Exception { 541 // Write some data 542 Table table = UTIL.getConnection().getTable(TABLE_NAME); 543 for (int i = 0; i < 10; i++) { 544 Put put = new Put(Bytes.toBytes(i)).addColumn(TEST_FAM, Bytes.toBytes("q"), Bytes.toBytes(i)); 545 table.put(put); 546 } 547 String snapshotName = "testAsyncSnapshotWillNotBlockSnapshotHFileCleaner01"; 548 Future<Void> future = 549 UTIL.getAdmin().snapshotAsync(new org.apache.hadoop.hbase.client.SnapshotDescription( 550 snapshotName, TABLE_NAME, SnapshotType.FLUSH)); 551 Waiter.waitFor(UTIL.getConfiguration(), 10 * 1000L, 200L, 552 () -> UTIL.getAdmin().listSnapshots(Pattern.compile(snapshotName)).size() == 1); 553 UTIL.waitFor(30000, () -> !master.getSnapshotManager().isTakingAnySnapshot()); 554 } 555}