blob: 575401545616e48f6b8b82b9a40c0f7e5514fee4 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.backup;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.PathFilter;
import org.apache.hadoop.hbase.ChoreService;
import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.Stoppable;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.RegionInfo;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.master.HMaster;
import org.apache.hadoop.hbase.master.cleaner.DirScanPool;
import org.apache.hadoop.hbase.master.cleaner.HFileCleaner;
import org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy;
import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.hadoop.hbase.regionserver.HRegionServer;
import org.apache.hadoop.hbase.regionserver.HStoreFile;
import org.apache.hadoop.hbase.testclassification.LargeTests;
import org.apache.hadoop.hbase.testclassification.MiscTests;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.CommonFSUtils;
import org.apache.hadoop.hbase.util.FSUtils;
import org.apache.hadoop.hbase.util.HFileArchiveTestingUtil;
import org.apache.hadoop.hbase.util.HFileArchiveUtil;
import org.apache.hadoop.hbase.util.StoppableImplementation;
import org.apache.hadoop.security.UserGroupInformation;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.TestName;
import org.mockito.ArgumentCaptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Test that the {@link HFileArchiver} correctly removes all the parts of a region when cleaning up
* a region
*/
@Category({LargeTests.class, MiscTests.class})
public class TestHFileArchiving {
@ClassRule
public static final HBaseClassTestRule CLASS_RULE =
HBaseClassTestRule.forClass(TestHFileArchiving.class);
private static final Logger LOG = LoggerFactory.getLogger(TestHFileArchiving.class);
private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
private static final byte[] TEST_FAM = Bytes.toBytes("fam");
private static DirScanPool POOL;
@Rule
public TestName name = new TestName();
/**
* Setup the config for the cluster
*/
@BeforeClass
public static void setupCluster() throws Exception {
setupConf(UTIL.getConfiguration());
UTIL.startMiniCluster();
// We don't want the cleaner to remove files. The tests do that.
UTIL.getMiniHBaseCluster().getMaster().getHFileCleaner().cancel(true);
POOL = new DirScanPool(UTIL.getConfiguration());
}
private static void setupConf(Configuration conf) {
// disable the ui
conf.setInt("hbase.regionsever.info.port", -1);
// drop the memstore size so we get flushes
conf.setInt("hbase.hregion.memstore.flush.size", 25000);
// disable major compactions
conf.setInt(HConstants.MAJOR_COMPACTION_PERIOD, 0);
// prevent aggressive region split
conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY,
ConstantSizeRegionSplitPolicy.class.getName());
}
@After
public void tearDown() throws Exception {
// cleanup the archive directory
clearArchiveDirectory();
}
@AfterClass
public static void cleanupTest() throws Exception {
UTIL.shutdownMiniCluster();
POOL.shutdownNow();
}
@Test
public void testArchiveStoreFilesDifferentFileSystemsWallWithSchemaPlainRoot() throws Exception {
String walDir = "mockFS://mockFSAuthority:9876/mockDir/wals/";
String baseDir = CommonFSUtils.getRootDir(UTIL.getConfiguration()).toString() + "/";
testArchiveStoreFilesDifferentFileSystems(walDir, baseDir,
HFileArchiver::archiveStoreFiles);
}
@Test
public void testArchiveStoreFilesDifferentFileSystemsWallNullPlainRoot() throws Exception {
String baseDir = CommonFSUtils.getRootDir(UTIL.getConfiguration()).toString() + "/";
testArchiveStoreFilesDifferentFileSystems(null, baseDir,
HFileArchiver::archiveStoreFiles);
}
@Test
public void testArchiveStoreFilesDifferentFileSystemsWallAndRootSame() throws Exception {
String baseDir = CommonFSUtils.getRootDir(UTIL.getConfiguration()).toString() + "/";
testArchiveStoreFilesDifferentFileSystems("/hbase/wals/", baseDir,
HFileArchiver::archiveStoreFiles);
}
private void testArchiveStoreFilesDifferentFileSystems(String walDir, String expectedBase,
ArchivingFunction<Configuration, FileSystem, RegionInfo, Path, byte[],
Collection<HStoreFile>> archivingFunction) throws IOException {
FileSystem mockedFileSystem = mock(FileSystem.class);
Configuration conf = new Configuration(UTIL.getConfiguration());
if(walDir != null) {
conf.set(CommonFSUtils.HBASE_WAL_DIR, walDir);
}
Path filePath = new Path("/mockDir/wals/mockFile");
when(mockedFileSystem.getScheme()).thenReturn("mockFS");
when(mockedFileSystem.mkdirs(any())).thenReturn(true);
when(mockedFileSystem.exists(any())).thenReturn(true);
RegionInfo mockedRegion = mock(RegionInfo.class);
TableName tableName = TableName.valueOf("mockTable");
when(mockedRegion.getTable()).thenReturn(tableName);
when(mockedRegion.getEncodedName()).thenReturn("mocked-region-encoded-name");
Path tableDir = new Path("mockFS://mockDir/tabledir");
byte[] family = Bytes.toBytes("testfamily");
HStoreFile mockedFile = mock(HStoreFile.class);
List<HStoreFile> list = new ArrayList<>();
list.add(mockedFile);
when(mockedFile.getPath()).thenReturn(filePath);
when(mockedFileSystem.rename(any(),any())).thenReturn(true);
archivingFunction.apply(conf, mockedFileSystem, mockedRegion, tableDir, family, list);
ArgumentCaptor<Path> pathCaptor = ArgumentCaptor.forClass(Path.class);
verify(mockedFileSystem, times(2)).rename(pathCaptor.capture(), any());
String expectedDir = expectedBase +
"archive/data/default/mockTable/mocked-region-encoded-name/testfamily/mockFile";
assertTrue(pathCaptor.getAllValues().get(0).toString().equals(expectedDir));
}
@FunctionalInterface
private interface ArchivingFunction<Configuration, FS, Region, Dir, Family, Files> {
void apply(Configuration config, FS fs, Region region, Dir dir, Family family, Files files)
throws IOException;
}
@Test
public void testArchiveRecoveredEditsWalDirNull() throws Exception {
testArchiveRecoveredEditsWalDirNullOrSame(null);
}
@Test
public void testArchiveRecoveredEditsWalDirSameFsStoreFiles() throws Exception {
testArchiveRecoveredEditsWalDirNullOrSame("/wal-dir");
}
private void testArchiveRecoveredEditsWalDirNullOrSame(String walDir) throws Exception {
String originalRootDir = UTIL.getConfiguration().get(HConstants.HBASE_DIR);
try {
String baseDir = "mockFS://mockFSAuthority:9876/hbase/";
UTIL.getConfiguration().set(HConstants.HBASE_DIR, baseDir);
testArchiveStoreFilesDifferentFileSystems(walDir, baseDir,
(conf, fs, region, dir, family, list) -> HFileArchiver
.archiveRecoveredEdits(conf, fs, region, family, list));
} finally {
UTIL.getConfiguration().set(HConstants.HBASE_DIR, originalRootDir);
}
}
@Test(expected = IOException.class)
public void testArchiveRecoveredEditsWrongFS() throws Exception {
String baseDir = CommonFSUtils.getRootDir(UTIL.getConfiguration()).toString() + "/";
//Internally, testArchiveStoreFilesDifferentFileSystems will pass a "mockedFS"
// to HFileArchiver.archiveRecoveredEdits, but since wal-dir is supposedly on same FS
// as root dir it would lead to conflicting FSes and an IOException is expected.
testArchiveStoreFilesDifferentFileSystems("/wal-dir", baseDir,
(conf, fs, region, dir, family, list) -> HFileArchiver
.archiveRecoveredEdits(conf, fs, region, family, list));
}
@Test
public void testArchiveRecoveredEditsWalDirDifferentFS() throws Exception {
String walDir = "mockFS://mockFSAuthority:9876/mockDir/wals/";
testArchiveStoreFilesDifferentFileSystems(walDir, walDir,
(conf, fs, region, dir, family, list) ->
HFileArchiver.archiveRecoveredEdits(conf, fs, region, family, list));
}
@Test
public void testRemoveRegionDirOnArchive() throws Exception {
final TableName tableName = TableName.valueOf(name.getMethodName());
UTIL.createTable(tableName, TEST_FAM);
final Admin admin = UTIL.getAdmin();
// get the current store files for the region
List<HRegion> servingRegions = UTIL.getHBaseCluster().getRegions(tableName);
// make sure we only have 1 region serving this table
assertEquals(1, servingRegions.size());
HRegion region = servingRegions.get(0);
// and load the table
UTIL.loadRegion(region, TEST_FAM);
// shutdown the table so we can manipulate the files
admin.disableTable(tableName);
FileSystem fs = UTIL.getTestFileSystem();
// now attempt to depose the region
Path rootDir = region.getRegionFileSystem().getTableDir().getParent();
Path regionDir = FSUtils.getRegionDirFromRootDir(rootDir, region.getRegionInfo());
HFileArchiver.archiveRegion(UTIL.getConfiguration(), fs, region.getRegionInfo());
// check for the existence of the archive directory and some files in it
Path archiveDir = HFileArchiveTestingUtil.getRegionArchiveDir(UTIL.getConfiguration(), region);
assertTrue(fs.exists(archiveDir));
// check to make sure the store directory was copied
FileStatus[] stores = fs.listStatus(archiveDir, new PathFilter() {
@Override
public boolean accept(Path p) {
if (p.getName().contains(HConstants.RECOVERED_EDITS_DIR)) {
return false;
}
return true;
}
});
assertTrue(stores.length == 1);
// make sure we archived the store files
FileStatus[] storeFiles = fs.listStatus(stores[0].getPath());
assertTrue(storeFiles.length > 0);
// then ensure the region's directory isn't present
assertFalse(fs.exists(regionDir));
UTIL.deleteTable(tableName);
}
/**
* Test that the region directory is removed when we archive a region without store files, but
* still has hidden files.
* @throws IOException throws an IOException if there's problem creating a table
* or if there's an issue with accessing FileSystem.
*/
@Test
public void testDeleteRegionWithNoStoreFiles() throws IOException {
final TableName tableName = TableName.valueOf(name.getMethodName());
UTIL.createTable(tableName, TEST_FAM);
// get the current store files for the region
List<HRegion> servingRegions = UTIL.getHBaseCluster().getRegions(tableName);
// make sure we only have 1 region serving this table
assertEquals(1, servingRegions.size());
HRegion region = servingRegions.get(0);
FileSystem fs = region.getRegionFileSystem().getFileSystem();
// make sure there are some files in the regiondir
Path rootDir = CommonFSUtils.getRootDir(fs.getConf());
Path regionDir = FSUtils.getRegionDirFromRootDir(rootDir, region.getRegionInfo());
FileStatus[] regionFiles = CommonFSUtils.listStatus(fs, regionDir, null);
Assert.assertNotNull("No files in the region directory", regionFiles);
if (LOG.isDebugEnabled()) {
List<Path> files = new ArrayList<>();
for (FileStatus file : regionFiles) {
files.add(file.getPath());
}
LOG.debug("Current files:" + files);
}
// delete the visible folders so we just have hidden files/folders
final PathFilter dirFilter = new FSUtils.DirFilter(fs);
PathFilter nonHidden = new PathFilter() {
@Override
public boolean accept(Path file) {
return dirFilter.accept(file) && !file.getName().startsWith(".");
}
};
FileStatus[] storeDirs = CommonFSUtils.listStatus(fs, regionDir, nonHidden);
for (FileStatus store : storeDirs) {
LOG.debug("Deleting store for test");
fs.delete(store.getPath(), true);
}
// then archive the region
HFileArchiver.archiveRegion(UTIL.getConfiguration(), fs, region.getRegionInfo());
// and check to make sure the region directoy got deleted
assertFalse("Region directory (" + regionDir + "), still exists.", fs.exists(regionDir));
UTIL.deleteTable(tableName);
}
private List<HRegion> initTableForArchivingRegions(TableName tableName) throws IOException {
final byte[][] splitKeys = new byte[][] {
Bytes.toBytes("b"), Bytes.toBytes("c"), Bytes.toBytes("d")
};
UTIL.createTable(tableName, TEST_FAM, splitKeys);
// get the current store files for the regions
List<HRegion> regions = UTIL.getHBaseCluster().getRegions(tableName);
// make sure we have 4 regions serving this table
assertEquals(4, regions.size());
// and load the table
try (Table table = UTIL.getConnection().getTable(tableName)) {
UTIL.loadTable(table, TEST_FAM);
}
// disable the table so that we can manipulate the files
UTIL.getAdmin().disableTable(tableName);
return regions;
}
@Test
public void testArchiveRegions() throws Exception {
final TableName tableName = TableName.valueOf(name.getMethodName());
List<HRegion> regions = initTableForArchivingRegions(tableName);
FileSystem fs = UTIL.getTestFileSystem();
// now attempt to depose the regions
Path rootDir = CommonFSUtils.getRootDir(UTIL.getConfiguration());
Path tableDir = CommonFSUtils.getTableDir(rootDir, regions.get(0).getRegionInfo().getTable());
List<Path> regionDirList = regions.stream()
.map(region -> FSUtils.getRegionDirFromTableDir(tableDir, region.getRegionInfo()))
.collect(Collectors.toList());
HFileArchiver.archiveRegions(UTIL.getConfiguration(), fs, rootDir, tableDir, regionDirList);
// check for the existence of the archive directory and some files in it
for (HRegion region : regions) {
Path archiveDir = HFileArchiveTestingUtil.getRegionArchiveDir(UTIL.getConfiguration(),
region);
assertTrue(fs.exists(archiveDir));
// check to make sure the store directory was copied
FileStatus[] stores = fs.listStatus(archiveDir,
p -> !p.getName().contains(HConstants.RECOVERED_EDITS_DIR));
assertTrue(stores.length == 1);
// make sure we archived the store files
FileStatus[] storeFiles = fs.listStatus(stores[0].getPath());
assertTrue(storeFiles.length > 0);
}
// then ensure the region's directories aren't present
for (Path regionDir: regionDirList) {
assertFalse(fs.exists(regionDir));
}
UTIL.deleteTable(tableName);
}
@Test(expected=IOException.class)
public void testArchiveRegionsWhenPermissionDenied() throws Exception {
final TableName tableName = TableName.valueOf(name.getMethodName());
List<HRegion> regions = initTableForArchivingRegions(tableName);
// now attempt to depose the regions
Path rootDir = CommonFSUtils.getRootDir(UTIL.getConfiguration());
Path tableDir = CommonFSUtils.getTableDir(rootDir, regions.get(0).getRegionInfo().getTable());
List<Path> regionDirList = regions.stream()
.map(region -> FSUtils.getRegionDirFromTableDir(tableDir, region.getRegionInfo()))
.collect(Collectors.toList());
// To create a permission denied error, we do archive regions as a non-current user
UserGroupInformation
ugi = UserGroupInformation.createUserForTesting("foo1234", new String[]{"group1"});
try {
ugi.doAs((PrivilegedExceptionAction<Void>) () -> {
FileSystem fs = UTIL.getTestFileSystem();
HFileArchiver.archiveRegions(UTIL.getConfiguration(), fs, rootDir, tableDir,
regionDirList);
return null;
});
} catch (IOException e) {
assertTrue(e.getCause().getMessage().contains("Permission denied"));
throw e;
} finally {
UTIL.deleteTable(tableName);
}
}
@Test
public void testArchiveOnTableDelete() throws Exception {
final TableName tableName = TableName.valueOf(name.getMethodName());
UTIL.createTable(tableName, TEST_FAM);
List<HRegion> servingRegions = UTIL.getHBaseCluster().getRegions(tableName);
// make sure we only have 1 region serving this table
assertEquals(1, servingRegions.size());
HRegion region = servingRegions.get(0);
// get the parent RS and monitor
HRegionServer hrs = UTIL.getRSForFirstRegionInTable(tableName);
FileSystem fs = hrs.getFileSystem();
// put some data on the region
LOG.debug("-------Loading table");
UTIL.loadRegion(region, TEST_FAM);
// get the hfiles in the region
List<HRegion> regions = hrs.getRegions(tableName);
assertEquals("More that 1 region for test table.", 1, regions.size());
region = regions.get(0);
// wait for all the compactions to complete
region.waitForFlushesAndCompactions();
// disable table to prevent new updates
UTIL.getAdmin().disableTable(tableName);
LOG.debug("Disabled table");
// remove all the files from the archive to get a fair comparison
clearArchiveDirectory();
// then get the current store files
byte[][]columns = region.getTableDescriptor().getColumnFamilyNames().toArray(new byte[0][]);
List<String> storeFiles = region.getStoreFileList(columns);
// then delete the table so the hfiles get archived
UTIL.deleteTable(tableName);
LOG.debug("Deleted table");
assertArchiveFiles(fs, storeFiles, 30000);
}
private void assertArchiveFiles(FileSystem fs, List<String> storeFiles, long timeout)
throws IOException {
long end = System.currentTimeMillis() + timeout;
Path archiveDir = HFileArchiveUtil.getArchivePath(UTIL.getConfiguration());
List<String> archivedFiles = new ArrayList<>();
// We have to ensure that the DeleteTableHandler is finished. HBaseAdmin.deleteXXX()
// can return before all files
// are archived. We should fix HBASE-5487 and fix synchronous operations from admin.
while (System.currentTimeMillis() < end) {
archivedFiles = getAllFileNames(fs, archiveDir);
if (archivedFiles.size() >= storeFiles.size()) {
break;
}
}
Collections.sort(storeFiles);
Collections.sort(archivedFiles);
LOG.debug("Store files:");
for (int i = 0; i < storeFiles.size(); i++) {
LOG.debug(i + " - " + storeFiles.get(i));
}
LOG.debug("Archive files:");
for (int i = 0; i < archivedFiles.size(); i++) {
LOG.debug(i + " - " + archivedFiles.get(i));
}
assertTrue("Archived files are missing some of the store files!",
archivedFiles.containsAll(storeFiles));
}
/**
* Test that the store files are archived when a column family is removed.
* @throws java.io.IOException if there's a problem creating a table.
* @throws java.lang.InterruptedException problem getting a RegionServer.
*/
@Test
public void testArchiveOnTableFamilyDelete() throws IOException, InterruptedException {
final TableName tableName = TableName.valueOf(name.getMethodName());
UTIL.createTable(tableName, new byte[][] {TEST_FAM, Bytes.toBytes("fam2")});
List<HRegion> servingRegions = UTIL.getHBaseCluster().getRegions(tableName);
// make sure we only have 1 region serving this table
assertEquals(1, servingRegions.size());
HRegion region = servingRegions.get(0);
// get the parent RS and monitor
HRegionServer hrs = UTIL.getRSForFirstRegionInTable(tableName);
FileSystem fs = hrs.getFileSystem();
// put some data on the region
LOG.debug("-------Loading table");
UTIL.loadRegion(region, TEST_FAM);
// get the hfiles in the region
List<HRegion> regions = hrs.getRegions(tableName);
assertEquals("More that 1 region for test table.", 1, regions.size());
region = regions.get(0);
// wait for all the compactions to complete
region.waitForFlushesAndCompactions();
// disable table to prevent new updates
UTIL.getAdmin().disableTable(tableName);
LOG.debug("Disabled table");
// remove all the files from the archive to get a fair comparison
clearArchiveDirectory();
// then get the current store files
byte[][]columns = region.getTableDescriptor().getColumnFamilyNames().toArray(new byte[0][]);
List<String> storeFiles = region.getStoreFileList(columns);
// then delete the table so the hfiles get archived
UTIL.getAdmin().deleteColumnFamily(tableName, TEST_FAM);
assertArchiveFiles(fs, storeFiles, 30000);
UTIL.deleteTable(tableName);
}
/**
* Test HFileArchiver.resolveAndArchive() race condition HBASE-7643
*/
@Test
public void testCleaningRace() throws Exception {
final long TEST_TIME = 20 * 1000;
final ChoreService choreService = new ChoreService("TEST_SERVER_NAME");
Configuration conf = UTIL.getMiniHBaseCluster().getMaster().getConfiguration();
Path rootDir = UTIL.getDataTestDirOnTestFS("testCleaningRace");
FileSystem fs = UTIL.getTestFileSystem();
Path archiveDir = new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY);
Path regionDir = new Path(CommonFSUtils.getTableDir(new Path("./"),
TableName.valueOf(name.getMethodName())), "abcdef");
Path familyDir = new Path(regionDir, "cf");
Path sourceRegionDir = new Path(rootDir, regionDir);
fs.mkdirs(sourceRegionDir);
Stoppable stoppable = new StoppableImplementation();
// The cleaner should be looping without long pauses to reproduce the race condition.
HFileCleaner cleaner = getHFileCleaner(stoppable, conf, fs, archiveDir);
assertNotNull("cleaner should not be null", cleaner);
try {
choreService.scheduleChore(cleaner);
// Keep creating/archiving new files while the cleaner is running in the other thread
long startTime = System.currentTimeMillis();
for (long fid = 0; (System.currentTimeMillis() - startTime) < TEST_TIME; ++fid) {
Path file = new Path(familyDir, String.valueOf(fid));
Path sourceFile = new Path(rootDir, file);
Path archiveFile = new Path(archiveDir, file);
fs.createNewFile(sourceFile);
try {
// Try to archive the file
HFileArchiver.archiveRegion(fs, rootDir,
sourceRegionDir.getParent(), sourceRegionDir);
// The archiver succeded, the file is no longer in the original location
// but it's in the archive location.
LOG.debug("hfile=" + fid + " should be in the archive");
assertTrue(fs.exists(archiveFile));
assertFalse(fs.exists(sourceFile));
} catch (IOException e) {
// The archiver is unable to archive the file. Probably HBASE-7643 race condition.
// in this case, the file should not be archived, and we should have the file
// in the original location.
LOG.debug("hfile=" + fid + " should be in the source location");
assertFalse(fs.exists(archiveFile));
assertTrue(fs.exists(sourceFile));
// Avoid to have this file in the next run
fs.delete(sourceFile, false);
}
}
} finally {
stoppable.stop("test end");
cleaner.cancel(true);
choreService.shutdown();
fs.delete(rootDir, true);
}
}
@Test
public void testArchiveRegionTableAndRegionDirsNull() throws IOException {
Path rootDir = UTIL.getDataTestDirOnTestFS("testCleaningRace");
FileSystem fileSystem = UTIL.getTestFileSystem();
// Try to archive the file but with null regionDir, can't delete sourceFile
assertFalse(HFileArchiver.archiveRegion(fileSystem, rootDir, null, null));
}
@Test
public void testArchiveRegionWithTableDirNull() throws IOException {
Path regionDir = new Path(CommonFSUtils.getTableDir(new Path("./"),
TableName.valueOf(name.getMethodName())), "xyzabc");
Path familyDir = new Path(regionDir, "rd");
Path rootDir = UTIL.getDataTestDirOnTestFS("testCleaningRace");
Path file = new Path(familyDir, "1");
Path sourceFile = new Path(rootDir, file);
FileSystem fileSystem = UTIL.getTestFileSystem();
fileSystem.createNewFile(sourceFile);
Path sourceRegionDir = new Path(rootDir, regionDir);
fileSystem.mkdirs(sourceRegionDir);
// Try to archive the file
assertFalse(HFileArchiver.archiveRegion(fileSystem, rootDir, null, sourceRegionDir));
assertFalse(fileSystem.exists(sourceRegionDir));
}
@Test
public void testArchiveRegionWithRegionDirNull() throws IOException {
Path regionDir = new Path(CommonFSUtils.getTableDir(new Path("./"),
TableName.valueOf(name.getMethodName())), "elgn4nf");
Path familyDir = new Path(regionDir, "rdar");
Path rootDir = UTIL.getDataTestDirOnTestFS("testCleaningRace");
Path file = new Path(familyDir, "2");
Path sourceFile = new Path(rootDir, file);
FileSystem fileSystem = UTIL.getTestFileSystem();
fileSystem.createNewFile(sourceFile);
Path sourceRegionDir = new Path(rootDir, regionDir);
fileSystem.mkdirs(sourceRegionDir);
// Try to archive the file but with null regionDir, can't delete sourceFile
assertFalse(HFileArchiver.archiveRegion(fileSystem, rootDir, sourceRegionDir.getParent(),
null));
assertTrue(fileSystem.exists(sourceRegionDir));
fileSystem.delete(sourceRegionDir, true);
}
// Avoid passing a null master to CleanerChore, see HBASE-21175
private HFileCleaner getHFileCleaner(Stoppable stoppable, Configuration conf, FileSystem fs,
Path archiveDir) throws IOException {
Map<String, Object> params = new HashMap<>();
params.put(HMaster.MASTER, UTIL.getMiniHBaseCluster().getMaster());
HFileCleaner cleaner = new HFileCleaner(1, stoppable, conf, fs, archiveDir, POOL);
return Objects.requireNonNull(cleaner);
}
private void clearArchiveDirectory() throws IOException {
UTIL.getTestFileSystem().delete(
new Path(UTIL.getDefaultRootDirPath(), HConstants.HFILE_ARCHIVE_DIRECTORY), true);
}
/**
* Get the names of all the files below the given directory
* @param fs the file system to inspect
* @param archiveDir the directory in which to look
* @return a list of all files in the directory and sub-directories
* @throws java.io.IOException throws IOException in case FS is unavailable
*/
private List<String> getAllFileNames(final FileSystem fs, Path archiveDir) throws IOException {
FileStatus[] files = CommonFSUtils.listStatus(fs, archiveDir, new PathFilter() {
@Override
public boolean accept(Path p) {
if (p.getName().contains(HConstants.RECOVERED_EDITS_DIR)) {
return false;
}
return true;
}
});
return recurseOnFiles(fs, files, new ArrayList<>());
}
/** Recursively lookup all the file names under the file[] array **/
private List<String> recurseOnFiles(FileSystem fs, FileStatus[] files, List<String> fileNames)
throws IOException {
if (files == null || files.length == 0) {
return fileNames;
}
for (FileStatus file : files) {
if (file.isDirectory()) {
recurseOnFiles(fs, CommonFSUtils.listStatus(fs, file.getPath(), null), fileNames);
} else {
fileNames.add(file.getPath().getName());
}
}
return fileNames;
}
}