blob: 502f43022e8d139b69e391eaf7fb6722f072f318 [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.master.janitor;
import static org.apache.hadoop.hbase.util.HFileArchiveTestingUtil.assertArchiveEqualToOriginal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Objects;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentSkipListMap;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.MetaMockingUtil;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
import org.apache.hadoop.hbase.client.RegionInfo;
import org.apache.hadoop.hbase.client.RegionInfoBuilder;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
import org.apache.hadoop.hbase.io.Reference;
import org.apache.hadoop.hbase.master.MasterServices;
import org.apache.hadoop.hbase.master.assignment.MockMasterServices;
import org.apache.hadoop.hbase.master.janitor.CatalogJanitor.SplitParentFirstComparator;
import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
import org.apache.hadoop.hbase.regionserver.ChunkCreator;
import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
import org.apache.hadoop.hbase.regionserver.MemStoreLAB;
import org.apache.hadoop.hbase.testclassification.MasterTests;
import org.apache.hadoop.hbase.testclassification.MediumTests;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.CommonFSUtils;
import org.apache.hadoop.hbase.util.HFileArchiveUtil;
import org.apache.zookeeper.KeeperException;
import org.junit.After;
import org.junit.Before;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Category({ MasterTests.class, MediumTests.class })
public class TestCatalogJanitor {
@ClassRule
public static final HBaseClassTestRule CLASS_RULE =
HBaseClassTestRule.forClass(TestCatalogJanitor.class);
private static final Logger LOG = LoggerFactory.getLogger(TestCatalogJanitor.class);
private static final HBaseTestingUtility HTU = new HBaseTestingUtility();
@Rule
public final TestName name = new TestName();
private MockMasterServices masterServices;
private CatalogJanitor janitor;
@BeforeClass
public static void beforeClass() throws Exception {
ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null,
MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT);
}
@Before
public void setup() throws IOException, KeeperException {
setRootDirAndCleanIt(HTU, this.name.getMethodName());
NavigableMap<ServerName, SortedSet<byte[]>> regionsToRegionServers =
new ConcurrentSkipListMap<ServerName, SortedSet<byte[]>>();
this.masterServices = new MockMasterServices(HTU.getConfiguration(), regionsToRegionServers);
this.masterServices.start(10, null);
this.janitor = new CatalogJanitor(masterServices);
}
@After
public void teardown() {
this.janitor.shutdown(true);
this.masterServices.stop("DONE");
}
private RegionInfo createRegionInfo(TableName tableName, byte[] startKey, byte[] endKey) {
return createRegionInfo(tableName, startKey, endKey, false);
}
private RegionInfo createRegionInfo(TableName tableName, byte[] startKey, byte[] endKey,
boolean split) {
return RegionInfoBuilder.newBuilder(tableName).setStartKey(startKey).setEndKey(endKey)
.setSplit(split).build();
}
/**
* Test clearing a split parent.
*/
@Test
public void testCleanParent() throws IOException, InterruptedException {
TableDescriptor td = createTableDescriptorForCurrentMethod();
// Create regions.
RegionInfo parent =
createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("eee"));
RegionInfo splita =
createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("ccc"));
RegionInfo splitb =
createRegionInfo(td.getTableName(), Bytes.toBytes("ccc"), Bytes.toBytes("eee"));
// Test that when both daughter regions are in place, that we do not remove the parent.
Result r = createResult(parent, splita, splitb);
// Add a reference under splitA directory so we don't clear out the parent.
Path rootdir = this.masterServices.getMasterFileSystem().getRootDir();
Path tabledir = CommonFSUtils.getTableDir(rootdir, td.getTableName());
Path parentdir = new Path(tabledir, parent.getEncodedName());
Path storedir = HRegionFileSystem.getStoreHomedir(tabledir, splita,
td.getColumnFamilies()[0].getName());
Reference ref = Reference.createTopReference(Bytes.toBytes("ccc"));
long now = System.currentTimeMillis();
// Reference name has this format: StoreFile#REF_NAME_PARSER
Path p = new Path(storedir, Long.toString(now) + "." + parent.getEncodedName());
FileSystem fs = this.masterServices.getMasterFileSystem().getFileSystem();
Path path = ref.write(fs, p);
assertTrue(fs.exists(path));
LOG.info("Created reference " + path);
// Add a parentdir for kicks so can check it gets removed by the catalogjanitor.
fs.mkdirs(parentdir);
assertFalse(CatalogJanitor.cleanParent(masterServices, parent, r));
ProcedureTestingUtility.waitAllProcedures(masterServices.getMasterProcedureExecutor());
assertTrue(fs.exists(parentdir));
// Remove the reference file and try again.
assertTrue(fs.delete(p, true));
assertTrue(CatalogJanitor.cleanParent(masterServices, parent, r));
// Parent cleanup is run async as a procedure. Make sure parentdir is removed.
ProcedureTestingUtility.waitAllProcedures(masterServices.getMasterProcedureExecutor());
assertTrue(!fs.exists(parentdir));
}
/**
* Make sure parent gets cleaned up even if daughter is cleaned up before it.
*/
@Test
public void testParentCleanedEvenIfDaughterGoneFirst() throws IOException, InterruptedException {
parentWithSpecifiedEndKeyCleanedEvenIfDaughterGoneFirst(this.name.getMethodName(),
Bytes.toBytes("eee"));
}
/**
* Make sure last parent with empty end key gets cleaned up even if daughter is cleaned up before
* it.
*/
@Test
public void testLastParentCleanedEvenIfDaughterGoneFirst()
throws IOException, InterruptedException {
parentWithSpecifiedEndKeyCleanedEvenIfDaughterGoneFirst(this.name.getMethodName(), new byte[0]);
}
/**
* @return A TableDescriptor with a tableName of current method name and a column family that is
* MockMasterServices.DEFAULT_COLUMN_FAMILY_NAME)
*/
private TableDescriptor createTableDescriptorForCurrentMethod() {
ColumnFamilyDescriptor columnFamilyDescriptor = ColumnFamilyDescriptorBuilder
.newBuilder(Bytes.toBytes(MockMasterServices.DEFAULT_COLUMN_FAMILY_NAME)).build();
return TableDescriptorBuilder.newBuilder(TableName.valueOf(this.name.getMethodName()))
.setColumnFamily(columnFamilyDescriptor).build();
}
/**
* Make sure parent with specified end key gets cleaned up even if daughter is cleaned up before
* it.
* @param rootDir the test case name, used as the HBase testing utility root
* @param lastEndKey the end key of the split parent
*/
private void parentWithSpecifiedEndKeyCleanedEvenIfDaughterGoneFirst(final String rootDir,
final byte[] lastEndKey) throws IOException, InterruptedException {
TableDescriptor td = createTableDescriptorForCurrentMethod();
// Create regions: aaa->{lastEndKey}, aaa->ccc, aaa->bbb, bbb->ccc, etc.
RegionInfo parent = createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), lastEndKey);
// Sleep a second else the encoded name on these regions comes out
// same for all with same start key and made in same second.
Thread.sleep(1001);
// Daughter a
RegionInfo splita =
createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("ccc"));
Thread.sleep(1001);
// Make daughters of daughter a; splitaa and splitab.
RegionInfo splitaa =
createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("bbb"));
RegionInfo splitab =
createRegionInfo(td.getTableName(), Bytes.toBytes("bbb"), Bytes.toBytes("ccc"));
// Daughter b
RegionInfo splitb = createRegionInfo(td.getTableName(), Bytes.toBytes("ccc"), lastEndKey);
Thread.sleep(1001);
// Make Daughters of daughterb; splitba and splitbb.
RegionInfo splitba =
createRegionInfo(td.getTableName(), Bytes.toBytes("ccc"), Bytes.toBytes("ddd"));
RegionInfo splitbb = createRegionInfo(td.getTableName(), Bytes.toBytes("ddd"), lastEndKey);
// First test that our Comparator works right up in CatalogJanitor.
SortedMap<RegionInfo, Result> regions =
new TreeMap<>(new CatalogJanitor.SplitParentFirstComparator());
// Now make sure that this regions map sorts as we expect it to.
regions.put(parent, createResult(parent, splita, splitb));
regions.put(splitb, createResult(splitb, splitba, splitbb));
regions.put(splita, createResult(splita, splitaa, splitab));
// Assert its properly sorted.
int index = 0;
for (Map.Entry<RegionInfo, Result> e : regions.entrySet()) {
if (index == 0) {
assertTrue(e.getKey().getEncodedName().equals(parent.getEncodedName()));
} else if (index == 1) {
assertTrue(e.getKey().getEncodedName().equals(splita.getEncodedName()));
} else if (index == 2) {
assertTrue(e.getKey().getEncodedName().equals(splitb.getEncodedName()));
}
index++;
}
// Now play around with the cleanParent function. Create a ref from splita up to the parent.
Path splitaRef =
createReferences(this.masterServices, td, parent, splita, Bytes.toBytes("ccc"), false);
// Make sure actual super parent sticks around because splita has a ref.
assertFalse(CatalogJanitor.cleanParent(masterServices, parent, regions.get(parent)));
// splitba, and split bb, do not have dirs in fs. That means that if
// we test splitb, it should get cleaned up.
assertTrue(CatalogJanitor.cleanParent(masterServices, splitb, regions.get(splitb)));
// Now remove ref from splita to parent... so parent can be let go and so
// the daughter splita can be split (can't split if still references).
// BUT make the timing such that the daughter gets cleaned up before we
// can get a chance to let go of the parent.
FileSystem fs = FileSystem.get(HTU.getConfiguration());
assertTrue(fs.delete(splitaRef, true));
// Create the refs from daughters of splita.
Path splitaaRef =
createReferences(this.masterServices, td, splita, splitaa, Bytes.toBytes("bbb"), false);
Path splitabRef =
createReferences(this.masterServices, td, splita, splitab, Bytes.toBytes("bbb"), true);
// Test splita. It should stick around because references from splitab, etc.
assertFalse(CatalogJanitor.cleanParent(masterServices, splita, regions.get(splita)));
// Now clean up parent daughter first. Remove references from its daughters.
assertTrue(fs.delete(splitaaRef, true));
assertTrue(fs.delete(splitabRef, true));
assertTrue(CatalogJanitor.cleanParent(masterServices, splita, regions.get(splita)));
// Super parent should get cleaned up now both splita and splitb are gone.
assertTrue(CatalogJanitor.cleanParent(masterServices, parent, regions.get(parent)));
}
/**
* CatalogJanitor.scan() should not clean parent regions if their own parents are still
* referencing them. This ensures that grandparent regions do not point to deleted parent regions.
*/
@Test
public void testScanDoesNotCleanRegionsWithExistingParents() throws Exception {
TableDescriptor td = createTableDescriptorForCurrentMethod();
// Create regions: aaa->{lastEndKey}, aaa->ccc, aaa->bbb, bbb->ccc, etc.
// Parent
RegionInfo parent =
createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), HConstants.EMPTY_BYTE_ARRAY, true);
// Sleep a second else the encoded name on these regions comes out
// same for all with same start key and made in same second.
Thread.sleep(1001);
// Daughter a
RegionInfo splita =
createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("ccc"), true);
Thread.sleep(1001);
// Make daughters of daughter a; splitaa and splitab.
RegionInfo splitaa =
createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("bbb"), false);
RegionInfo splitab =
createRegionInfo(td.getTableName(), Bytes.toBytes("bbb"), Bytes.toBytes("ccc"), false);
// Daughter b
RegionInfo splitb =
createRegionInfo(td.getTableName(), Bytes.toBytes("ccc"), HConstants.EMPTY_BYTE_ARRAY);
Thread.sleep(1001);
// Parent has daughters splita and splitb. Splita has daughters splitaa and splitab.
final Map<RegionInfo, Result> splitParents = new TreeMap<>(new SplitParentFirstComparator());
splitParents.put(parent, createResult(parent, splita, splitb));
// simulate that splita goes offline when it is split
splita = RegionInfoBuilder.newBuilder(splita).setOffline(true).build();
splitParents.put(splita, createResult(splita, splitaa, splitab));
final Map<RegionInfo, Result> mergedRegions = new TreeMap<>();
CatalogJanitor spy = spy(this.janitor);
Report report = new Report();
report.count = 10;
report.mergedRegions.putAll(mergedRegions);
report.splitParents.putAll(splitParents);
doReturn(report).when(spy).scanForReport();
// Create ref from splita to parent
LOG.info("parent=" + parent.getShortNameToLog() + ", splita=" + splita.getShortNameToLog());
Path splitaRef =
createReferences(this.masterServices, td, parent, splita, Bytes.toBytes("ccc"), false);
LOG.info("Created reference " + splitaRef);
// Parent and splita should not be removed because a reference from splita to parent.
int gcs = spy.scan();
assertEquals(0, gcs);
// Now delete the ref
FileSystem fs = FileSystem.get(HTU.getConfiguration());
assertTrue(fs.delete(splitaRef, true));
// now, both parent, and splita can be deleted
gcs = spy.scan();
assertEquals(2, gcs);
}
/**
* Test that we correctly archive all the storefiles when a region is deleted
* @throws Exception
*/
@Test
public void testSplitParentFirstComparator() {
SplitParentFirstComparator comp = new SplitParentFirstComparator();
TableDescriptor td = createTableDescriptorForCurrentMethod();
/*
* Region splits: rootRegion --- firstRegion --- firstRegiona | |- firstRegionb | |- lastRegion
* --- lastRegiona --- lastRegionaa | |- lastRegionab |- lastRegionb rootRegion : [] - []
* firstRegion : [] - bbb lastRegion : bbb - [] firstRegiona : [] - aaa firstRegionb : aaa - bbb
* lastRegiona : bbb - ddd lastRegionb : ddd - []
*/
// root region
RegionInfo rootRegion = createRegionInfo(td.getTableName(), HConstants.EMPTY_START_ROW,
HConstants.EMPTY_END_ROW, true);
RegionInfo firstRegion =
createRegionInfo(td.getTableName(), HConstants.EMPTY_START_ROW, Bytes.toBytes("bbb"), true);
RegionInfo lastRegion =
createRegionInfo(td.getTableName(), Bytes.toBytes("bbb"), HConstants.EMPTY_END_ROW, true);
assertTrue(comp.compare(rootRegion, rootRegion) == 0);
assertTrue(comp.compare(firstRegion, firstRegion) == 0);
assertTrue(comp.compare(lastRegion, lastRegion) == 0);
assertTrue(comp.compare(rootRegion, firstRegion) < 0);
assertTrue(comp.compare(rootRegion, lastRegion) < 0);
assertTrue(comp.compare(firstRegion, lastRegion) < 0);
// first region split into a, b
RegionInfo firstRegiona =
createRegionInfo(td.getTableName(), HConstants.EMPTY_START_ROW, Bytes.toBytes("aaa"), true);
RegionInfo firstRegionb =
createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("bbb"), true);
// last region split into a, b
RegionInfo lastRegiona =
createRegionInfo(td.getTableName(), Bytes.toBytes("bbb"), Bytes.toBytes("ddd"), true);
RegionInfo lastRegionb =
createRegionInfo(td.getTableName(), Bytes.toBytes("ddd"), HConstants.EMPTY_END_ROW, true);
assertTrue(comp.compare(firstRegiona, firstRegiona) == 0);
assertTrue(comp.compare(firstRegionb, firstRegionb) == 0);
assertTrue(comp.compare(rootRegion, firstRegiona) < 0);
assertTrue(comp.compare(rootRegion, firstRegionb) < 0);
assertTrue(comp.compare(firstRegion, firstRegiona) < 0);
assertTrue(comp.compare(firstRegion, firstRegionb) < 0);
assertTrue(comp.compare(firstRegiona, firstRegionb) < 0);
assertTrue(comp.compare(lastRegiona, lastRegiona) == 0);
assertTrue(comp.compare(lastRegionb, lastRegionb) == 0);
assertTrue(comp.compare(rootRegion, lastRegiona) < 0);
assertTrue(comp.compare(rootRegion, lastRegionb) < 0);
assertTrue(comp.compare(lastRegion, lastRegiona) < 0);
assertTrue(comp.compare(lastRegion, lastRegionb) < 0);
assertTrue(comp.compare(lastRegiona, lastRegionb) < 0);
assertTrue(comp.compare(firstRegiona, lastRegiona) < 0);
assertTrue(comp.compare(firstRegiona, lastRegionb) < 0);
assertTrue(comp.compare(firstRegionb, lastRegiona) < 0);
assertTrue(comp.compare(firstRegionb, lastRegionb) < 0);
RegionInfo lastRegionaa =
createRegionInfo(td.getTableName(), Bytes.toBytes("bbb"), Bytes.toBytes("ccc"), false);
RegionInfo lastRegionab =
createRegionInfo(td.getTableName(), Bytes.toBytes("ccc"), Bytes.toBytes("ddd"), false);
assertTrue(comp.compare(lastRegiona, lastRegionaa) < 0);
assertTrue(comp.compare(lastRegiona, lastRegionab) < 0);
assertTrue(comp.compare(lastRegionaa, lastRegionab) < 0);
}
@Test
public void testArchiveOldRegion() throws Exception {
// Create regions.
TableDescriptor td = createTableDescriptorForCurrentMethod();
RegionInfo parent =
createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("eee"));
RegionInfo splita =
createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("ccc"));
RegionInfo splitb =
createRegionInfo(td.getTableName(), Bytes.toBytes("ccc"), Bytes.toBytes("eee"));
// Test that when both daughter regions are in place, that we do not
// remove the parent.
Result parentMetaRow = createResult(parent, splita, splitb);
FileSystem fs = FileSystem.get(HTU.getConfiguration());
Path rootdir = this.masterServices.getMasterFileSystem().getRootDir();
// have to set the root directory since we use it in HFileDisposer to figure out to get to the
// archive directory. Otherwise, it just seems to pick the first root directory it can find (so
// the single test passes, but when the full suite is run, things get borked).
CommonFSUtils.setRootDir(fs.getConf(), rootdir);
Path tabledir = CommonFSUtils.getTableDir(rootdir, td.getTableName());
Path storedir = HRegionFileSystem.getStoreHomedir(tabledir, parent,
td.getColumnFamilies()[0].getName());
Path storeArchive = HFileArchiveUtil.getStoreArchivePath(this.masterServices.getConfiguration(),
parent, tabledir, td.getColumnFamilies()[0].getName());
LOG.debug("Table dir:" + tabledir);
LOG.debug("Store dir:" + storedir);
LOG.debug("Store archive dir:" + storeArchive);
// add a couple of store files that we can check for
FileStatus[] mockFiles = addMockStoreFiles(2, this.masterServices, storedir);
// get the current store files for comparison
FileStatus[] storeFiles = fs.listStatus(storedir);
int index = 0;
for (FileStatus file : storeFiles) {
LOG.debug("Have store file:" + file.getPath());
assertEquals("Got unexpected store file", mockFiles[index].getPath(),
storeFiles[index].getPath());
index++;
}
// do the cleaning of the parent
assertTrue(CatalogJanitor.cleanParent(masterServices, parent, parentMetaRow));
Path parentDir = new Path(tabledir, parent.getEncodedName());
// Cleanup procedure runs async. Wait till it done.
ProcedureTestingUtility.waitAllProcedures(masterServices.getMasterProcedureExecutor());
assertTrue(!fs.exists(parentDir));
LOG.debug("Finished cleanup of parent region");
// and now check to make sure that the files have actually been archived
FileStatus[] archivedStoreFiles = fs.listStatus(storeArchive);
logFiles("archived files", storeFiles);
logFiles("archived files", archivedStoreFiles);
assertArchiveEqualToOriginal(storeFiles, archivedStoreFiles, fs);
// cleanup
CommonFSUtils.delete(fs, rootdir, true);
}
/**
* @param description description of the files for logging
* @param storeFiles the status of the files to log
*/
private void logFiles(String description, FileStatus[] storeFiles) {
LOG.debug("Current " + description + ": ");
for (FileStatus file : storeFiles) {
LOG.debug(Objects.toString(file.getPath()));
}
}
/**
* Test that if a store file with the same name is present as those already backed up cause the
* already archived files to be timestamped backup
*/
@Test
public void testDuplicateHFileResolution() throws Exception {
TableDescriptor td = createTableDescriptorForCurrentMethod();
// Create regions.
RegionInfo parent =
createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("eee"));
RegionInfo splita =
createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("ccc"));
RegionInfo splitb =
createRegionInfo(td.getTableName(), Bytes.toBytes("ccc"), Bytes.toBytes("eee"));
// Test that when both daughter regions are in place, that we do not
// remove the parent.
Result r = createResult(parent, splita, splitb);
FileSystem fs = FileSystem.get(HTU.getConfiguration());
Path rootdir = this.masterServices.getMasterFileSystem().getRootDir();
// Have to set the root directory since we use it in HFileDisposer to figure out to get to the
// archive directory. Otherwise, it just seems to pick the first root directory it can find (so
// the single test passes, but when the full suite is run, things get borked).
CommonFSUtils.setRootDir(fs.getConf(), rootdir);
Path tabledir = CommonFSUtils.getTableDir(rootdir, parent.getTable());
Path storedir = HRegionFileSystem.getStoreHomedir(tabledir, parent,
td.getColumnFamilies()[0].getName());
LOG.info("Old root:" + rootdir);
LOG.info("Old table:" + tabledir);
LOG.info("Old store:" + storedir);
Path storeArchive = HFileArchiveUtil.getStoreArchivePath(this.masterServices.getConfiguration(),
parent, tabledir, td.getColumnFamilies()[0].getName());
LOG.info("Old archive:" + storeArchive);
// enable archiving, make sure that files get archived
addMockStoreFiles(2, this.masterServices, storedir);
// get the current store files for comparison
FileStatus[] storeFiles = fs.listStatus(storedir);
// Do the cleaning of the parent
assertTrue(CatalogJanitor.cleanParent(masterServices, parent, r));
Path parentDir = new Path(tabledir, parent.getEncodedName());
ProcedureTestingUtility.waitAllProcedures(masterServices.getMasterProcedureExecutor());
assertTrue(!fs.exists(parentDir));
// And now check to make sure that the files have actually been archived
FileStatus[] archivedStoreFiles = fs.listStatus(storeArchive);
assertArchiveEqualToOriginal(storeFiles, archivedStoreFiles, fs);
// now add store files with the same names as before to check backup
// enable archiving, make sure that files get archived
addMockStoreFiles(2, this.masterServices, storedir);
// Do the cleaning of the parent
assertTrue(CatalogJanitor.cleanParent(masterServices, parent, r));
// Cleanup procedure runs async. Wait till it done.
ProcedureTestingUtility.waitAllProcedures(masterServices.getMasterProcedureExecutor());
assertTrue(!fs.exists(parentDir));
// and now check to make sure that the files have actually been archived
archivedStoreFiles = fs.listStatus(storeArchive);
assertArchiveEqualToOriginal(storeFiles, archivedStoreFiles, fs, true);
}
@Test
public void testAlreadyRunningStatus() throws Exception {
int numberOfThreads = 2;
List<Integer> gcValues = new ArrayList<>();
Thread[] threads = new Thread[numberOfThreads];
for (int i = 0; i < numberOfThreads; i++) {
threads[i] = new Thread(() -> {
try {
gcValues.add(janitor.scan());
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
for (int i = 0; i < numberOfThreads; i++) {
threads[i].start();
}
for (int i = 0; i < numberOfThreads; i++) {
threads[i].join();
}
assertTrue("One janitor.scan() call should have returned -1", gcValues.contains(-1));
}
private FileStatus[] addMockStoreFiles(int count, MasterServices services, Path storedir)
throws IOException {
// get the existing store files
FileSystem fs = services.getMasterFileSystem().getFileSystem();
fs.mkdirs(storedir);
// create the store files in the parent
for (int i = 0; i < count; i++) {
Path storeFile = new Path(storedir, "_store" + i);
FSDataOutputStream dos = fs.create(storeFile, true);
dos.writeBytes("Some data: " + i);
dos.close();
}
LOG.debug("Adding " + count + " store files to the storedir:" + storedir);
// make sure the mock store files are there
FileStatus[] storeFiles = fs.listStatus(storedir);
assertEquals("Didn't have expected store files", count, storeFiles.length);
return storeFiles;
}
private String setRootDirAndCleanIt(final HBaseTestingUtility htu, final String subdir)
throws IOException {
Path testdir = htu.getDataTestDir(subdir);
FileSystem fs = FileSystem.get(htu.getConfiguration());
if (fs.exists(testdir)) assertTrue(fs.delete(testdir, true));
CommonFSUtils.setRootDir(htu.getConfiguration(), testdir);
return CommonFSUtils.getRootDir(htu.getConfiguration()).toString();
}
private Path createReferences(final MasterServices services, final TableDescriptor td,
final RegionInfo parent, final RegionInfo daughter, final byte[] midkey, final boolean top)
throws IOException {
Path rootdir = services.getMasterFileSystem().getRootDir();
Path tabledir = CommonFSUtils.getTableDir(rootdir, parent.getTable());
Path storedir = HRegionFileSystem.getStoreHomedir(tabledir, daughter,
td.getColumnFamilies()[0].getName());
Reference ref =
top ? Reference.createTopReference(midkey) : Reference.createBottomReference(midkey);
long now = System.currentTimeMillis();
// Reference name has this format: StoreFile#REF_NAME_PARSER
Path p = new Path(storedir, Long.toString(now) + "." + parent.getEncodedName());
FileSystem fs = services.getMasterFileSystem().getFileSystem();
ref.write(fs, p);
return p;
}
private Result createResult(final RegionInfo parent, final RegionInfo a, final RegionInfo b)
throws IOException {
return MetaMockingUtil.getMetaTableRowResult(parent, null, a, b);
}
}