Skip to content

Commit f68b61a

Browse files
authored
HBASE-27495 Improve HFileLinkCleaner to validate back reference links ahead the next traverse (#4887)
Signed-off-by: Peter Somogyi <psomogyi@apache.org> Signed-off-by: Wellington Ramos Chevreuil <wchevreuil@apache.org>
1 parent 7b0ac64 commit f68b61a

File tree

2 files changed

+105
-42
lines changed

2 files changed

+105
-42
lines changed

hbase-server/src/main/java/org/apache/hadoop/hbase/master/cleaner/HFileLinkCleaner.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,23 @@ public boolean isFileDeletable(FileStatus fStat) {
9090
}
9191

9292
// HFile is deletable only if has no links
93-
Path backRefDir = null;
93+
Path backRefDir = HFileLink.getBackReferencesDir(parentDir, filePath.getName());
9494
try {
95-
backRefDir = HFileLink.getBackReferencesDir(parentDir, filePath.getName());
96-
return CommonFSUtils.listStatus(fs, backRefDir) == null;
95+
FileStatus[] fileStatuses = CommonFSUtils.listStatus(fs, backRefDir);
96+
// for empty reference directory, retain the logic to be deletable
97+
if (fileStatuses == null) {
98+
return true;
99+
}
100+
// reuse the found back reference files, check if the forward reference exists.
101+
// with this optimization, the chore could save one round compute time if we're visiting
102+
// the archive HFile earlier than the HFile Link
103+
for (FileStatus fileStatus : fileStatuses) {
104+
if (!isFileDeletable(fileStatus)) {
105+
return false;
106+
}
107+
}
108+
// all the found back reference files are clear, we can delete it.
109+
return true;
97110
} catch (IOException e) {
98111
if (LOG.isDebugEnabled()) {
99112
LOG.debug("Couldn't get the references, not deleting file, just in case. filePath="

hbase-server/src/test/java/org/apache/hadoop/hbase/master/cleaner/TestHFileLinkCleaner.java

Lines changed: 89 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
import org.apache.hadoop.hbase.util.HFileArchiveUtil;
4040
import org.apache.hadoop.hbase.util.MockServer;
4141
import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
42+
import org.junit.After;
4243
import org.junit.AfterClass;
44+
import org.junit.Before;
4345
import org.junit.BeforeClass;
4446
import org.junit.ClassRule;
4547
import org.junit.Rule;
@@ -57,9 +59,28 @@ public class TestHFileLinkCleaner {
5759
public static final HBaseClassTestRule CLASS_RULE =
5860
HBaseClassTestRule.forClass(TestHFileLinkCleaner.class);
5961

62+
private Configuration conf;
63+
private Path rootDir;
64+
private FileSystem fs;
65+
private TableName tableName;
66+
private TableName tableLinkName;
67+
private String hfileName;
68+
private String familyName;
69+
private RegionInfo hri;
70+
private RegionInfo hriLink;
71+
private Path archiveDir;
72+
private Path archiveStoreDir;
73+
private Path familyPath;
74+
private Path hfilePath;
75+
private Path familyLinkPath;
76+
private String hfileLinkName;
77+
private Path linkBackRefDir;
78+
private Path linkBackRef;
79+
private FileStatus[] backRefs;
80+
private HFileCleaner cleaner;
6081
private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
61-
6282
private static DirScanPool POOL;
83+
private static final long TTL = 1000;
6384

6485
@Rule
6586
public TestName name = new TestName();
@@ -74,49 +95,71 @@ public static void tearDown() {
7495
POOL.shutdownNow();
7596
}
7697

77-
@Test
78-
public void testHFileLinkCleaning() throws Exception {
79-
Configuration conf = TEST_UTIL.getConfiguration();
98+
@Before
99+
public void configureDirectoriesAndLinks() throws IOException {
100+
conf = TEST_UTIL.getConfiguration();
80101
CommonFSUtils.setRootDir(conf, TEST_UTIL.getDataTestDir());
81102
conf.set(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS, HFileLinkCleaner.class.getName());
82-
Path rootDir = CommonFSUtils.getRootDir(conf);
83-
FileSystem fs = FileSystem.get(conf);
103+
rootDir = CommonFSUtils.getRootDir(conf);
104+
fs = FileSystem.get(conf);
84105

85-
final TableName tableName = TableName.valueOf(name.getMethodName());
86-
final TableName tableLinkName = TableName.valueOf(name.getMethodName() + "-link");
87-
final String hfileName = "1234567890";
88-
final String familyName = "cf";
106+
tableName = TableName.valueOf(name.getMethodName());
107+
tableLinkName = TableName.valueOf(name.getMethodName() + "-link");
108+
hfileName = "1234567890";
109+
familyName = "cf";
89110

90-
RegionInfo hri = RegionInfoBuilder.newBuilder(tableName).build();
91-
RegionInfo hriLink = RegionInfoBuilder.newBuilder(tableLinkName).build();
111+
hri = RegionInfoBuilder.newBuilder(tableName).build();
112+
hriLink = RegionInfoBuilder.newBuilder(tableLinkName).build();
92113

93-
Path archiveDir = HFileArchiveUtil.getArchivePath(conf);
94-
Path archiveStoreDir =
114+
archiveDir = HFileArchiveUtil.getArchivePath(conf);
115+
archiveStoreDir =
95116
HFileArchiveUtil.getStoreArchivePath(conf, tableName, hri.getEncodedName(), familyName);
96117

97118
// Create hfile /hbase/table-link/region/cf/getEncodedName.HFILE(conf);
98-
Path familyPath = getFamilyDirPath(archiveDir, tableName, hri.getEncodedName(), familyName);
119+
familyPath = getFamilyDirPath(archiveDir, tableName, hri.getEncodedName(), familyName);
99120
fs.mkdirs(familyPath);
100-
Path hfilePath = new Path(familyPath, hfileName);
121+
hfilePath = new Path(familyPath, hfileName);
101122
fs.createNewFile(hfilePath);
102123

124+
createLink(true);
125+
126+
// Initialize cleaner
127+
conf.setLong(TimeToLiveHFileCleaner.TTL_CONF_KEY, TTL);
128+
Server server = new DummyServer();
129+
cleaner = new HFileCleaner(1000, server, conf, fs, archiveDir, POOL);
130+
}
131+
132+
private void createLink(boolean createBackReference) throws IOException {
103133
// Create link to hfile
104-
Path familyLinkPath =
105-
getFamilyDirPath(rootDir, tableLinkName, hriLink.getEncodedName(), familyName);
134+
familyLinkPath = getFamilyDirPath(rootDir, tableLinkName, hriLink.getEncodedName(), familyName);
106135
fs.mkdirs(familyLinkPath);
107-
HFileLink.create(conf, fs, familyLinkPath, hri, hfileName);
108-
Path linkBackRefDir = HFileLink.getBackReferencesDir(archiveStoreDir, hfileName);
136+
hfileLinkName = HFileLink.create(conf, fs, familyLinkPath, hri, hfileName, createBackReference);
137+
linkBackRefDir = HFileLink.getBackReferencesDir(archiveStoreDir, hfileName);
109138
assertTrue(fs.exists(linkBackRefDir));
110-
FileStatus[] backRefs = fs.listStatus(linkBackRefDir);
139+
backRefs = fs.listStatus(linkBackRefDir);
111140
assertEquals(1, backRefs.length);
112-
Path linkBackRef = backRefs[0].getPath();
141+
linkBackRef = backRefs[0].getPath();
142+
}
113143

114-
// Initialize cleaner
115-
final long ttl = 1000;
116-
conf.setLong(TimeToLiveHFileCleaner.TTL_CONF_KEY, ttl);
117-
Server server = new DummyServer();
118-
HFileCleaner cleaner = new HFileCleaner(1000, server, conf, fs, archiveDir, POOL);
144+
@After
145+
public void cleanup() throws IOException, InterruptedException {
146+
// HFile can be removed
147+
Thread.sleep(TTL * 2);
148+
cleaner.chore();
149+
assertFalse("HFile should be deleted", fs.exists(hfilePath));
150+
// Remove everything
151+
for (int i = 0; i < 4; ++i) {
152+
Thread.sleep(TTL * 2);
153+
cleaner.chore();
154+
}
155+
assertFalse("HFile should be deleted",
156+
fs.exists(CommonFSUtils.getTableDir(archiveDir, tableName)));
157+
assertFalse("Link should be deleted",
158+
fs.exists(CommonFSUtils.getTableDir(archiveDir, tableLinkName)));
159+
}
119160

161+
@Test
162+
public void testHFileLinkCleaning() throws Exception {
120163
// Link backref cannot be removed
121164
cleaner.chore();
122165
assertTrue(fs.exists(linkBackRef));
@@ -127,21 +170,28 @@ public void testHFileLinkCleaning() throws Exception {
127170
CommonFSUtils.getTableDir(archiveDir, tableLinkName));
128171
cleaner.chore();
129172
assertFalse("Link should be deleted", fs.exists(linkBackRef));
173+
}
130174

131-
// HFile can be removed
132-
Thread.sleep(ttl * 2);
175+
@Test
176+
public void testHFileLinkByRemovingReference() throws Exception {
177+
// Link backref cannot be removed
133178
cleaner.chore();
134-
assertFalse("HFile should be deleted", fs.exists(hfilePath));
179+
assertTrue(fs.exists(linkBackRef));
180+
assertTrue(fs.exists(hfilePath));
135181

136-
// Remove everything
137-
for (int i = 0; i < 4; ++i) {
138-
Thread.sleep(ttl * 2);
139-
cleaner.chore();
140-
}
141-
assertFalse("HFile should be deleted",
142-
fs.exists(CommonFSUtils.getTableDir(archiveDir, tableName)));
143-
assertFalse("Link should be deleted",
144-
fs.exists(CommonFSUtils.getTableDir(archiveDir, tableLinkName)));
182+
// simulate after removing the reference in data directory, the Link backref can be removed
183+
fs.delete(new Path(familyLinkPath, hfileLinkName), false);
184+
cleaner.chore();
185+
assertFalse("Link should be deleted", fs.exists(linkBackRef));
186+
}
187+
188+
@Test
189+
public void testHFileLinkEmptyBackReferenceDirectory() throws Exception {
190+
// simulate and remove the back reference
191+
fs.delete(linkBackRef, false);
192+
assertTrue("back reference directory still exists", fs.exists(linkBackRefDir));
193+
cleaner.chore();
194+
assertFalse("back reference directory should be deleted", fs.exists(linkBackRefDir));
145195
}
146196

147197
private static Path getFamilyDirPath(final Path rootDir, final TableName table,

0 commit comments

Comments
 (0)