blob: 35a3a79af50dc08b39600bd3cd01bfdfabce4ca0 [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;
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.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
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.ChoreService;
import org.apache.hadoop.hbase.CoordinatedStateManager;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.MetaMockingUtil;
import org.apache.hadoop.hbase.NotAllMetaRegionsOnlineException;
import org.apache.hadoop.hbase.Server;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableDescriptors;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.ClusterConnection;
import org.apache.hadoop.hbase.client.HConnectionTestingUtility;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.coordination.BaseCoordinatedStateManager;
import org.apache.hadoop.hbase.coordination.SplitLogManagerCoordination;
import org.apache.hadoop.hbase.coordination.SplitLogManagerCoordination.SplitLogManagerDetails;
import org.apache.hadoop.hbase.io.Reference;
import org.apache.hadoop.hbase.master.CatalogJanitor.SplitParentFirstComparator;
import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiRequest;
import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiResponse;
import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutateRequest;
import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutateResponse;
import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionAction;
import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionActionResult;
import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ResultOrException;
import org.apache.hadoop.hbase.regionserver.HStore;
import org.apache.hadoop.hbase.testclassification.MasterTests;
import org.apache.hadoop.hbase.testclassification.SmallTests;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.FSUtils;
import org.apache.hadoop.hbase.util.HFileArchiveUtil;
import org.apache.hadoop.hbase.util.Triple;
import org.apache.hadoop.hbase.zookeeper.MetaTableLocator;
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import com.google.protobuf.RpcController;
import com.google.protobuf.ServiceException;
@Category({MasterTests.class, SmallTests.class})
public class TestCatalogJanitor {
private static final Log LOG = LogFactory.getLog(TestCatalogJanitor.class);
/**
* Pseudo server for below tests.
* Be sure to call stop on the way out else could leave some mess around.
*/
class MockServer implements Server {
private final ClusterConnection connection;
private final Configuration c;
MockServer(final HBaseTestingUtility htu)
throws NotAllMetaRegionsOnlineException, IOException, InterruptedException {
this.c = htu.getConfiguration();
ClientProtos.ClientService.BlockingInterface ri =
Mockito.mock(ClientProtos.ClientService.BlockingInterface.class);
MutateResponse.Builder builder = MutateResponse.newBuilder();
builder.setProcessed(true);
try {
Mockito.when(ri.mutate(
(RpcController)Mockito.any(), (MutateRequest)Mockito.any())).
thenReturn(builder.build());
} catch (ServiceException se) {
throw ProtobufUtil.getRemoteException(se);
}
try {
Mockito.when(ri.multi(
(RpcController)Mockito.any(), (MultiRequest)Mockito.any())).
thenAnswer(new Answer<MultiResponse>() {
@Override
public MultiResponse answer(InvocationOnMock invocation) throws Throwable {
return buildMultiResponse( (MultiRequest)invocation.getArguments()[1]);
}
});
} catch (ServiceException se) {
throw ProtobufUtil.getRemoteException(se);
}
// Mock an ClusterConnection and a AdminProtocol implementation. Have the
// ClusterConnection return the HRI. Have the HRI return a few mocked up responses
// to make our test work.
this.connection =
HConnectionTestingUtility.getMockedConnectionAndDecorate(this.c,
Mockito.mock(AdminProtos.AdminService.BlockingInterface.class), ri,
ServerName.valueOf("example.org,12345,6789"),
HRegionInfo.FIRST_META_REGIONINFO);
// Set hbase.rootdir into test dir.
FileSystem.get(this.c);
Path rootdir = FSUtils.getRootDir(this.c);
FSUtils.setRootDir(this.c, rootdir);
Mockito.mock(AdminProtos.AdminService.BlockingInterface.class);
}
@Override
public ClusterConnection getConnection() {
return this.connection;
}
@Override
public MetaTableLocator getMetaTableLocator() {
return null;
}
@Override
public Configuration getConfiguration() {
return this.c;
}
@Override
public ServerName getServerName() {
return ServerName.valueOf("mockserver.example.org", 1234, -1L);
}
@Override
public ZooKeeperWatcher getZooKeeper() {
return null;
}
@Override
public CoordinatedStateManager getCoordinatedStateManager() {
BaseCoordinatedStateManager m = Mockito.mock(BaseCoordinatedStateManager.class);
SplitLogManagerCoordination c = Mockito.mock(SplitLogManagerCoordination.class);
Mockito.when(m.getSplitLogManagerCoordination()).thenReturn(c);
SplitLogManagerDetails d = Mockito.mock(SplitLogManagerDetails.class);
Mockito.when(c.getDetails()).thenReturn(d);
return m;
}
@Override
public void abort(String why, Throwable e) {
//no-op
}
@Override
public boolean isAborted() {
return false;
}
@Override
public boolean isStopped() {
return false;
}
@Override
public void stop(String why) {
}
@Override
public ChoreService getChoreService() {
return null;
}
@Override
public ClusterConnection getClusterConnection() {
// TODO Auto-generated method stub
return null;
}
}
/**
* Mock MasterServices for tests below.
*/
class MockMasterServices extends MockNoopMasterServices {
private final MasterFileSystem mfs;
private final AssignmentManager asm;
private final Server server;
MockMasterServices(final Server server) throws IOException {
this.server = server;
this.mfs = new MasterFileSystem(this);
this.asm = Mockito.mock(AssignmentManager.class);
}
@Override
public AssignmentManager getAssignmentManager() {
return this.asm;
}
@Override
public MasterFileSystem getMasterFileSystem() {
return this.mfs;
}
@Override
public Configuration getConfiguration() {
return server.getConfiguration();
}
@Override
public ServerName getServerName() {
return server.getServerName();
}
@Override
public TableDescriptors getTableDescriptors() {
return new TableDescriptors() {
@Override
public HTableDescriptor remove(TableName tablename) throws IOException {
// noop
return null;
}
@Override
public Map<String, HTableDescriptor> getAll() throws IOException {
// noop
return null;
}
@Override public Map<String, HTableDescriptor> getAllDescriptors() throws IOException {
// noop
return null;
}
@Override
public HTableDescriptor get(TableName tablename)
throws IOException {
return createHTableDescriptor();
}
@Override
public Map<String, HTableDescriptor> getByNamespace(String name) throws IOException {
return null;
}
@Override
public void add(HTableDescriptor htd) throws IOException {
// noop
}
@Override
public void setCacheOn() throws IOException {
}
@Override
public void setCacheOff() throws IOException {
}
};
}
}
@Test
public void testCleanParent() throws IOException, InterruptedException {
HBaseTestingUtility htu = new HBaseTestingUtility();
setRootDirAndCleanIt(htu, "testCleanParent");
Server server = new MockServer(htu);
try {
MasterServices services = new MockMasterServices(server);
CatalogJanitor janitor = new CatalogJanitor(server, services);
// Create regions.
HTableDescriptor htd = new HTableDescriptor(TableName.valueOf("table"));
htd.addFamily(new HColumnDescriptor("f"));
HRegionInfo parent =
new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
Bytes.toBytes("eee"));
HRegionInfo splita =
new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
Bytes.toBytes("ccc"));
HRegionInfo splitb =
new HRegionInfo(htd.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 = services.getMasterFileSystem().getRootDir();
Path tabledir =
FSUtils.getTableDir(rootdir, htd.getTableName());
Path storedir = HStore.getStoreHomedir(tabledir, splita,
htd.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 = services.getMasterFileSystem().getFileSystem();
Path path = ref.write(fs, p);
assertTrue(fs.exists(path));
assertFalse(janitor.cleanParent(parent, r));
// Remove the reference file and try again.
assertTrue(fs.delete(p, true));
assertTrue(janitor.cleanParent(parent, r));
} finally {
server.stop("shutdown");
}
}
/**
* Make sure parent gets cleaned up even if daughter is cleaned up before it.
* @throws IOException
* @throws InterruptedException
*/
@Test
public void testParentCleanedEvenIfDaughterGoneFirst()
throws IOException, InterruptedException {
parentWithSpecifiedEndKeyCleanedEvenIfDaughterGoneFirst(
"testParentCleanedEvenIfDaughterGoneFirst", Bytes.toBytes("eee"));
}
/**
* Make sure last parent with empty end key gets cleaned up even if daughter is cleaned up before it.
* @throws IOException
* @throws InterruptedException
*/
@Test
public void testLastParentCleanedEvenIfDaughterGoneFirst()
throws IOException, InterruptedException {
parentWithSpecifiedEndKeyCleanedEvenIfDaughterGoneFirst(
"testLastParentCleanedEvenIfDaughterGoneFirst", new byte[0]);
}
/**
* 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
* @throws IOException
* @throws InterruptedException
*/
private void parentWithSpecifiedEndKeyCleanedEvenIfDaughterGoneFirst(
final String rootDir, final byte[] lastEndKey)
throws IOException, InterruptedException {
HBaseTestingUtility htu = new HBaseTestingUtility();
setRootDirAndCleanIt(htu, rootDir);
Server server = new MockServer(htu);
MasterServices services = new MockMasterServices(server);
CatalogJanitor janitor = new CatalogJanitor(server, services);
final HTableDescriptor htd = createHTableDescriptor();
// Create regions: aaa->{lastEndKey}, aaa->ccc, aaa->bbb, bbb->ccc, etc.
// Parent
HRegionInfo parent = new HRegionInfo(htd.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
HRegionInfo splita = new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
Bytes.toBytes("ccc"));
Thread.sleep(1001);
// Make daughters of daughter a; splitaa and splitab.
HRegionInfo splitaa = new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
Bytes.toBytes("bbb"));
HRegionInfo splitab = new HRegionInfo(htd.getTableName(), Bytes.toBytes("bbb"),
Bytes.toBytes("ccc"));
// Daughter b
HRegionInfo splitb = new HRegionInfo(htd.getTableName(), Bytes.toBytes("ccc"),
lastEndKey);
Thread.sleep(1001);
// Make Daughters of daughterb; splitba and splitbb.
HRegionInfo splitba = new HRegionInfo(htd.getTableName(), Bytes.toBytes("ccc"),
Bytes.toBytes("ddd"));
HRegionInfo splitbb = new HRegionInfo(htd.getTableName(), Bytes.toBytes("ddd"),
lastEndKey);
// First test that our Comparator works right up in CatalogJanitor.
// Just fo kicks.
SortedMap<HRegionInfo, Result> regions =
new TreeMap<HRegionInfo, Result>(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<HRegionInfo, 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(services, htd, parent, splita, Bytes.toBytes("ccc"), false);
// Make sure actual super parent sticks around because splita has a ref.
assertFalse(janitor.cleanParent(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(janitor.cleanParent(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(services, htd, splita, splitaa, Bytes.toBytes("bbb"), false);
Path splitabRef =
createReferences(services, htd, splita, splitab, Bytes.toBytes("bbb"), true);
// Test splita. It should stick around because references from splitab, etc.
assertFalse(janitor.cleanParent(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(janitor.cleanParent(splita, regions.get(splita)));
// Super parent should get cleaned up now both splita and splitb are gone.
assertTrue(janitor.cleanParent(parent, regions.get(parent)));
services.stop("test finished");
janitor.cancel(true);
}
/**
* CatalogJanitor.scan() should not clean parent regions if their own
* parents are still referencing them. This ensures that grandfather regions
* do not point to deleted parent regions.
*/
@Test
public void testScanDoesNotCleanRegionsWithExistingParents() throws Exception {
HBaseTestingUtility htu = new HBaseTestingUtility();
setRootDirAndCleanIt(htu, "testScanDoesNotCleanRegionsWithExistingParents");
Server server = new MockServer(htu);
MasterServices services = new MockMasterServices(server);
final HTableDescriptor htd = createHTableDescriptor();
// Create regions: aaa->{lastEndKey}, aaa->ccc, aaa->bbb, bbb->ccc, etc.
// Parent
HRegionInfo parent = new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
new byte[0], 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
HRegionInfo splita = new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
Bytes.toBytes("ccc"), true);
Thread.sleep(1001);
// Make daughters of daughter a; splitaa and splitab.
HRegionInfo splitaa = new HRegionInfo(htd.getTableName(), Bytes.toBytes("aaa"),
Bytes.toBytes("bbb"), false);
HRegionInfo splitab = new HRegionInfo(htd.getTableName(), Bytes.toBytes("bbb"),
Bytes.toBytes("ccc"), false);
// Daughter b
HRegionInfo splitb = new HRegionInfo(htd.getTableName(), Bytes.toBytes("ccc"),
new byte[0]);
Thread.sleep(1001);
final Map<HRegionInfo, Result> splitParents =
new TreeMap<HRegionInfo, Result>(new SplitParentFirstComparator());
splitParents.put(parent, createResult(parent, splita, splitb));
splita.setOffline(true); //simulate that splita goes offline when it is split
splitParents.put(splita, createResult(splita, splitaa,splitab));
final Map<HRegionInfo, Result> mergedRegions = new TreeMap<HRegionInfo, Result>();
CatalogJanitor janitor = spy(new CatalogJanitor(server, services));
doReturn(new Triple<Integer, Map<HRegionInfo, Result>, Map<HRegionInfo, Result>>(
10, mergedRegions, splitParents)).when(janitor)
.getMergedRegionsAndSplitParents();
//create ref from splita to parent
Path splitaRef =
createReferences(services, htd, parent, splita, Bytes.toBytes("ccc"), false);
//parent and A should not be removed
assertEquals(0, janitor.scan());
//now delete the ref
FileSystem fs = FileSystem.get(htu.getConfiguration());
assertTrue(fs.delete(splitaRef, true));
//now, both parent, and splita can be deleted
assertEquals(2, janitor.scan());
services.stop("test finished");
janitor.cancel(true);
}
/**
* Test that we correctly archive all the storefiles when a region is deleted
* @throws Exception
*/
@Test
public void testSplitParentFirstComparator() {
SplitParentFirstComparator comp = new SplitParentFirstComparator();
final HTableDescriptor htd = createHTableDescriptor();
/* 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
HRegionInfo rootRegion = new HRegionInfo(htd.getTableName(),
HConstants.EMPTY_START_ROW,
HConstants.EMPTY_END_ROW, true);
HRegionInfo firstRegion = new HRegionInfo(htd.getTableName(),
HConstants.EMPTY_START_ROW,
Bytes.toBytes("bbb"), true);
HRegionInfo lastRegion = new HRegionInfo(htd.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
HRegionInfo firstRegiona = new HRegionInfo(htd.getTableName(),
HConstants.EMPTY_START_ROW,
Bytes.toBytes("aaa"), true);
HRegionInfo firstRegionb = new HRegionInfo(htd.getTableName(),
Bytes.toBytes("aaa"),
Bytes.toBytes("bbb"), true);
//last region split into a, b
HRegionInfo lastRegiona = new HRegionInfo(htd.getTableName(),
Bytes.toBytes("bbb"),
Bytes.toBytes("ddd"), true);
HRegionInfo lastRegionb = new HRegionInfo(htd.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);
HRegionInfo lastRegionaa = new HRegionInfo(htd.getTableName(),
Bytes.toBytes("bbb"),
Bytes.toBytes("ccc"), false);
HRegionInfo lastRegionab = new HRegionInfo(htd.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 {
String table = "table";
HBaseTestingUtility htu = new HBaseTestingUtility();
setRootDirAndCleanIt(htu, "testCleanParent");
Server server = new MockServer(htu);
MasterServices services = new MockMasterServices(server);
// create the janitor
CatalogJanitor janitor = new CatalogJanitor(server, services);
// Create regions.
HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(table));
htd.addFamily(new HColumnDescriptor("f"));
HRegionInfo parent = new HRegionInfo(htd.getTableName(),
Bytes.toBytes("aaa"), Bytes.toBytes("eee"));
HRegionInfo splita = new HRegionInfo(htd.getTableName(),
Bytes.toBytes("aaa"), Bytes.toBytes("ccc"));
HRegionInfo splitb = new HRegionInfo(htd.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 = services.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).
FSUtils.setRootDir(fs.getConf(), rootdir);
Path tabledir = FSUtils.getTableDir(rootdir, htd.getTableName());
Path storedir = HStore.getStoreHomedir(tabledir, parent, htd.getColumnFamilies()[0].getName());
Path storeArchive = HFileArchiveUtil.getStoreArchivePath(services.getConfiguration(), parent,
tabledir, htd.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, services, 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(janitor.cleanParent(parent, parentMetaRow));
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
FSUtils.delete(fs, rootdir, true);
services.stop("Test finished");
server.stop("Test finished");
janitor.cancel(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(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 {
String table = "table";
HBaseTestingUtility htu = new HBaseTestingUtility();
setRootDirAndCleanIt(htu, "testCleanParent");
Server server = new MockServer(htu);
MasterServices services = new MockMasterServices(server);
// create the janitor
CatalogJanitor janitor = new CatalogJanitor(server, services);
// Create regions.
HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(table));
htd.addFamily(new HColumnDescriptor("f"));
HRegionInfo parent = new HRegionInfo(htd.getTableName(),
Bytes.toBytes("aaa"), Bytes.toBytes("eee"));
HRegionInfo splita = new HRegionInfo(htd.getTableName(),
Bytes.toBytes("aaa"), Bytes.toBytes("ccc"));
HRegionInfo splitb = new HRegionInfo(htd.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 = services.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).
FSUtils.setRootDir(fs.getConf(), rootdir);
Path tabledir = FSUtils.getTableDir(rootdir, parent.getTable());
Path storedir = HStore.getStoreHomedir(tabledir, parent, htd.getColumnFamilies()[0].getName());
System.out.println("Old root:" + rootdir);
System.out.println("Old table:" + tabledir);
System.out.println("Old store:" + storedir);
Path storeArchive = HFileArchiveUtil.getStoreArchivePath(services.getConfiguration(), parent,
tabledir, htd.getColumnFamilies()[0].getName());
System.out.println("Old archive:" + storeArchive);
// enable archiving, make sure that files get archived
addMockStoreFiles(2, services, storedir);
// get the current store files for comparison
FileStatus[] storeFiles = fs.listStatus(storedir);
// do the cleaning of the parent
assertTrue(janitor.cleanParent(parent, r));
// 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, services, storedir);
// do the cleaning of the parent
assertTrue(janitor.cleanParent(parent, r));
// and now check to make sure that the files have actually been archived
archivedStoreFiles = fs.listStatus(storeArchive);
assertArchiveEqualToOriginal(storeFiles, archivedStoreFiles, fs, true);
// cleanup
services.stop("Test finished");
server.stop("shutdown");
janitor.cancel(true);
}
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));
FSUtils.setRootDir(htu.getConfiguration(), testdir);
return FSUtils.getRootDir(htu.getConfiguration()).toString();
}
/**
* @param services Master services instance.
* @param htd
* @param parent
* @param daughter
* @param midkey
* @param top True if we are to write a 'top' reference.
* @return Path to reference we created.
* @throws IOException
*/
private Path createReferences(final MasterServices services,
final HTableDescriptor htd, final HRegionInfo parent,
final HRegionInfo daughter, final byte [] midkey, final boolean top)
throws IOException {
Path rootdir = services.getMasterFileSystem().getRootDir();
Path tabledir = FSUtils.getTableDir(rootdir, parent.getTable());
Path storedir = HStore.getStoreHomedir(tabledir, daughter,
htd.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 HRegionInfo parent, final HRegionInfo a,
final HRegionInfo b)
throws IOException {
return MetaMockingUtil.getMetaTableRowResult(parent, null, a, b);
}
private HTableDescriptor createHTableDescriptor() {
HTableDescriptor htd = new HTableDescriptor(TableName.valueOf("t"));
htd.addFamily(new HColumnDescriptor("f"));
return htd;
}
private MultiResponse buildMultiResponse(MultiRequest req) {
MultiResponse.Builder builder = MultiResponse.newBuilder();
RegionActionResult.Builder regionActionResultBuilder =
RegionActionResult.newBuilder();
ResultOrException.Builder roeBuilder = ResultOrException.newBuilder();
for (RegionAction regionAction: req.getRegionActionList()) {
regionActionResultBuilder.clear();
for (ClientProtos.Action action: regionAction.getActionList()) {
roeBuilder.clear();
roeBuilder.setResult(ClientProtos.Result.getDefaultInstance());
roeBuilder.setIndex(action.getIndex());
regionActionResultBuilder.addResultOrException(roeBuilder.build());
}
builder.addRegionActionResult(regionActionResultBuilder.build());
}
return builder.build();
}
}