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.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.io.IOException; 025import org.apache.hadoop.conf.Configuration; 026import org.apache.hadoop.fs.FileStatus; 027import org.apache.hadoop.fs.FileSystem; 028import org.apache.hadoop.fs.Path; 029import org.apache.hadoop.hbase.HBaseClassTestRule; 030import org.apache.hadoop.hbase.HBaseTestingUtil; 031import org.apache.hadoop.hbase.Server; 032import org.apache.hadoop.hbase.TableName; 033import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 034import org.apache.hadoop.hbase.client.RegionInfo; 035import org.apache.hadoop.hbase.client.RegionInfoBuilder; 036import org.apache.hadoop.hbase.io.HFileLink; 037import org.apache.hadoop.hbase.regionserver.HRegionFileSystem; 038import org.apache.hadoop.hbase.regionserver.StoreContext; 039import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTracker; 040import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory; 041import org.apache.hadoop.hbase.testclassification.MasterTests; 042import org.apache.hadoop.hbase.testclassification.MediumTests; 043import org.apache.hadoop.hbase.util.CommonFSUtils; 044import org.apache.hadoop.hbase.util.HFileArchiveUtil; 045import org.apache.hadoop.hbase.util.MockServer; 046import org.apache.hadoop.hbase.zookeeper.ZKWatcher; 047import org.junit.After; 048import org.junit.AfterClass; 049import org.junit.Before; 050import org.junit.BeforeClass; 051import org.junit.ClassRule; 052import org.junit.Rule; 053import org.junit.Test; 054import org.junit.experimental.categories.Category; 055import org.junit.rules.TestName; 056 057/** 058 * Test the HFileLink Cleaner. HFiles with links cannot be deleted until a link is present. 059 */ 060@Category({ MasterTests.class, MediumTests.class }) 061public class TestHFileLinkCleaner { 062 063 @ClassRule 064 public static final HBaseClassTestRule CLASS_RULE = 065 HBaseClassTestRule.forClass(TestHFileLinkCleaner.class); 066 067 private Configuration conf; 068 private Path rootDir; 069 private FileSystem fs; 070 private TableName tableName; 071 private TableName tableLinkName; 072 private String hfileName; 073 private String familyName; 074 private RegionInfo hri; 075 private RegionInfo hriLink; 076 private Path archiveDir; 077 private Path archiveStoreDir; 078 private Path familyPath; 079 private Path hfilePath; 080 private Path familyLinkPath; 081 private String hfileLinkName; 082 private Path linkBackRefDir; 083 private Path linkBackRef; 084 private FileStatus[] backRefs; 085 private HFileCleaner cleaner; 086 private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 087 private static DirScanPool POOL; 088 private static final long TTL = 1000; 089 090 @Rule 091 public TestName name = new TestName(); 092 093 @BeforeClass 094 public static void setUp() { 095 POOL = DirScanPool.getHFileCleanerScanPool(TEST_UTIL.getConfiguration()); 096 } 097 098 @AfterClass 099 public static void tearDown() { 100 POOL.shutdownNow(); 101 } 102 103 @Before 104 public void configureDirectoriesAndLinks() throws IOException { 105 conf = TEST_UTIL.getConfiguration(); 106 CommonFSUtils.setRootDir(conf, TEST_UTIL.getDataTestDir()); 107 conf.set(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS, HFileLinkCleaner.class.getName()); 108 rootDir = CommonFSUtils.getRootDir(conf); 109 fs = FileSystem.get(conf); 110 111 tableName = TableName.valueOf(name.getMethodName()); 112 tableLinkName = TableName.valueOf(name.getMethodName() + "-link"); 113 hfileName = "1234567890"; 114 familyName = "cf"; 115 116 hri = RegionInfoBuilder.newBuilder(tableName).build(); 117 hriLink = RegionInfoBuilder.newBuilder(tableLinkName).build(); 118 119 archiveDir = HFileArchiveUtil.getArchivePath(conf); 120 archiveStoreDir = 121 HFileArchiveUtil.getStoreArchivePath(conf, tableName, hri.getEncodedName(), familyName); 122 123 // Create hfile /hbase/table-link/region/cf/getEncodedName.HFILE(conf); 124 familyPath = getFamilyDirPath(archiveDir, tableName, hri.getEncodedName(), familyName); 125 fs.mkdirs(familyPath); 126 hfilePath = new Path(familyPath, hfileName); 127 fs.createNewFile(hfilePath); 128 129 HRegionFileSystem regionFS = HRegionFileSystem.create(conf, fs, 130 CommonFSUtils.getTableDir(rootDir, tableLinkName), hriLink); 131 StoreFileTracker sft = StoreFileTrackerFactory.create(conf, true, 132 StoreContext.getBuilder() 133 .withFamilyStoreDirectoryPath(new Path(regionFS.getRegionDir(), familyName)) 134 .withColumnFamilyDescriptor(ColumnFamilyDescriptorBuilder.of(familyName)) 135 .withRegionFileSystem(regionFS).build()); 136 createLink(sft, true); 137 138 // Initialize cleaner 139 conf.setLong(TimeToLiveHFileCleaner.TTL_CONF_KEY, TTL); 140 Server server = new DummyServer(); 141 cleaner = new HFileCleaner(1000, server, conf, fs, archiveDir, POOL); 142 } 143 144 private void createLink(StoreFileTracker sft, boolean createBackReference) throws IOException { 145 // Create link to hfile 146 familyLinkPath = getFamilyDirPath(rootDir, tableLinkName, hriLink.getEncodedName(), familyName); 147 fs.mkdirs(familyLinkPath); 148 hfileLinkName = 149 sft.createHFileLink(hri.getTable(), hri.getEncodedName(), hfileName, createBackReference); 150 linkBackRefDir = HFileLink.getBackReferencesDir(archiveStoreDir, hfileName); 151 assertTrue(fs.exists(linkBackRefDir)); 152 backRefs = fs.listStatus(linkBackRefDir); 153 assertEquals(1, backRefs.length); 154 linkBackRef = backRefs[0].getPath(); 155 } 156 157 @After 158 public void cleanup() throws IOException, InterruptedException { 159 // HFile can be removed 160 Thread.sleep(TTL * 2); 161 cleaner.chore(); 162 assertFalse("HFile should be deleted", fs.exists(hfilePath)); 163 // Remove everything 164 for (int i = 0; i < 4; ++i) { 165 Thread.sleep(TTL * 2); 166 cleaner.chore(); 167 } 168 assertFalse("HFile should be deleted", 169 fs.exists(CommonFSUtils.getTableDir(archiveDir, tableName))); 170 assertFalse("Link should be deleted", 171 fs.exists(CommonFSUtils.getTableDir(archiveDir, tableLinkName))); 172 } 173 174 @Test 175 public void testHFileLinkCleaning() throws Exception { 176 // Link backref cannot be removed 177 cleaner.chore(); 178 // CommonFSUtils. 179 assertTrue(fs.exists(linkBackRef)); 180 assertTrue(fs.exists(hfilePath)); 181 182 // Link backref can be removed 183 fs.rename(CommonFSUtils.getTableDir(rootDir, tableLinkName), 184 CommonFSUtils.getTableDir(archiveDir, tableLinkName)); 185 cleaner.chore(); 186 assertFalse("Link should be deleted", fs.exists(linkBackRef)); 187 } 188 189 @Test 190 public void testHFileLinkByRemovingReference() throws Exception { 191 // Link backref cannot be removed 192 cleaner.chore(); 193 assertTrue(fs.exists(linkBackRef)); 194 assertTrue(fs.exists(hfilePath)); 195 196 // simulate after removing the reference in data directory, the Link backref can be removed 197 fs.delete(new Path(familyLinkPath, hfileLinkName), false); 198 cleaner.chore(); 199 assertFalse("Link should be deleted", fs.exists(linkBackRef)); 200 } 201 202 @Test 203 public void testHFileLinkEmptyBackReferenceDirectory() throws Exception { 204 // simulate and remove the back reference 205 fs.delete(linkBackRef, false); 206 assertTrue("back reference directory still exists", fs.exists(linkBackRefDir)); 207 cleaner.chore(); 208 assertFalse("back reference directory should be deleted", fs.exists(linkBackRefDir)); 209 } 210 211 private static Path getFamilyDirPath(final Path rootDir, final TableName table, 212 final String region, final String family) { 213 return new Path(new Path(CommonFSUtils.getTableDir(rootDir, table), region), family); 214 } 215 216 static class DummyServer extends MockServer { 217 218 @Override 219 public Configuration getConfiguration() { 220 return TEST_UTIL.getConfiguration(); 221 } 222 223 @Override 224 public ZKWatcher getZooKeeper() { 225 try { 226 return new ZKWatcher(getConfiguration(), "dummy server", this); 227 } catch (IOException e) { 228 e.printStackTrace(); 229 } 230 return null; 231 } 232 } 233}