1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.backup;
19
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertTrue;
23
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Objects;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.apache.hadoop.conf.Configuration;
35 import org.apache.hadoop.fs.FileStatus;
36 import org.apache.hadoop.fs.FileSystem;
37 import org.apache.hadoop.fs.Path;
38 import org.apache.hadoop.fs.PathFilter;
39 import org.apache.hadoop.hbase.ChoreService;
40 import org.apache.hadoop.hbase.HBaseTestingUtility;
41 import org.apache.hadoop.hbase.HConstants;
42 import org.apache.hadoop.hbase.Stoppable;
43 import org.apache.hadoop.hbase.TableName;
44 import org.apache.hadoop.hbase.client.Admin;
45 import org.apache.hadoop.hbase.master.HMaster;
46 import org.apache.hadoop.hbase.master.cleaner.DirScanPool;
47 import org.apache.hadoop.hbase.master.cleaner.HFileCleaner;
48 import org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy;
49 import org.apache.hadoop.hbase.regionserver.HRegion;
50 import org.apache.hadoop.hbase.regionserver.HRegionServer;
51 import org.apache.hadoop.hbase.regionserver.Region;
52 import org.apache.hadoop.hbase.testclassification.MediumTests;
53 import org.apache.hadoop.hbase.util.Bytes;
54 import org.apache.hadoop.hbase.util.FSUtils;
55 import org.apache.hadoop.hbase.util.HFileArchiveTestingUtil;
56 import org.apache.hadoop.hbase.util.HFileArchiveUtil;
57 import org.apache.hadoop.hbase.util.StoppableImplementation;
58 import org.junit.After;
59 import org.junit.AfterClass;
60 import org.junit.Assert;
61 import org.junit.BeforeClass;
62 import org.junit.Test;
63 import org.junit.experimental.categories.Category;
64
65
66
67
68
69 @Category(MediumTests.class)
70 public class TestHFileArchiving {
71
72 private static final Log LOG = LogFactory.getLog(TestHFileArchiving.class);
73 private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
74 private static final byte[] TEST_FAM = Bytes.toBytes("fam");
75
76 private static DirScanPool POOL;
77
78
79
80
81 @BeforeClass
82 public static void setupCluster() throws Exception {
83 setupConf(UTIL.getConfiguration());
84 UTIL.startMiniCluster();
85
86
87 UTIL.getMiniHBaseCluster().getMaster().getHFileCleaner().cancel(true);
88
89 POOL = new DirScanPool(UTIL.getConfiguration());
90 }
91
92 private static void setupConf(Configuration conf) {
93
94 conf.setInt("hbase.regionsever.info.port", -1);
95
96 conf.setInt("hbase.hregion.memstore.flush.size", 25000);
97
98 conf.setInt(HConstants.MAJOR_COMPACTION_PERIOD, 0);
99
100
101 conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY,
102 ConstantSizeRegionSplitPolicy.class.getName());
103 }
104
105 @After
106 public void tearDown() throws Exception {
107
108 clearArchiveDirectory();
109 }
110
111 @AfterClass
112 public static void cleanupTest() throws Exception {
113 UTIL.shutdownMiniCluster();
114 POOL.shutdownNow();
115 }
116
117 @Test
118 public void testRemovesRegionDirOnArchive() throws Exception {
119 TableName TABLE_NAME =
120 TableName.valueOf("testRemovesRegionDirOnArchive");
121 UTIL.createTable(TABLE_NAME, TEST_FAM);
122
123 final Admin admin = UTIL.getHBaseAdmin();
124
125
126 List<HRegion> servingRegions = UTIL.getHBaseCluster().getRegions(TABLE_NAME);
127
128 assertEquals(1, servingRegions.size());
129 HRegion region = servingRegions.get(0);
130
131
132 UTIL.loadRegion(region, TEST_FAM);
133
134
135 admin.disableTable(TABLE_NAME);
136
137 FileSystem fs = UTIL.getTestFileSystem();
138
139
140 Path rootDir = region.getRegionFileSystem().getTableDir().getParent();
141 Path regionDir = FSUtils.getRegionDirFromRootDir(rootDir, region.getRegionInfo());
142
143 HFileArchiver.archiveRegion(UTIL.getConfiguration(), fs, region.getRegionInfo());
144
145
146 Path archiveDir = HFileArchiveTestingUtil.getRegionArchiveDir(UTIL.getConfiguration(), region);
147 assertTrue(fs.exists(archiveDir));
148
149
150
151 FileStatus[] stores = fs.listStatus(archiveDir, new PathFilter() {
152 @Override
153 public boolean accept(Path p) {
154 if (p.getName().contains(HConstants.RECOVERED_EDITS_DIR)) {
155 return false;
156 }
157 return true;
158 }
159 });
160 assertTrue(stores.length == 1);
161
162
163 FileStatus[] storeFiles = fs.listStatus(stores[0].getPath());
164 assertTrue(storeFiles.length > 0);
165
166
167 assertFalse(fs.exists(regionDir));
168
169 UTIL.deleteTable(TABLE_NAME);
170 }
171
172
173
174
175
176
177 @Test
178 public void testDeleteRegionWithNoStoreFiles() throws Exception {
179 TableName TABLE_NAME =
180 TableName.valueOf("testDeleteRegionWithNoStoreFiles");
181 UTIL.createTable(TABLE_NAME, TEST_FAM);
182
183
184 List<HRegion> servingRegions = UTIL.getHBaseCluster().getRegions(TABLE_NAME);
185
186 assertEquals(1, servingRegions.size());
187 HRegion region = servingRegions.get(0);
188
189 FileSystem fs = region.getRegionFileSystem().getFileSystem();
190
191
192 Path rootDir = FSUtils.getRootDir(fs.getConf());
193 Path regionDir = FSUtils.getRegionDirFromRootDir(rootDir, region.getRegionInfo());
194 FileStatus[] regionFiles = FSUtils.listStatus(fs, regionDir, null);
195 Assert.assertNotNull("No files in the region directory", regionFiles);
196 if (LOG.isDebugEnabled()) {
197 List<Path> files = new ArrayList<Path>();
198 for (FileStatus file : regionFiles) {
199 files.add(file.getPath());
200 }
201 LOG.debug("Current files:" + files);
202 }
203
204 final PathFilter dirFilter = new FSUtils.DirFilter(fs);
205 PathFilter nonHidden = new PathFilter() {
206 @Override
207 public boolean accept(Path file) {
208 return dirFilter.accept(file) && !file.getName().toString().startsWith(".");
209 }
210 };
211 FileStatus[] storeDirs = FSUtils.listStatus(fs, regionDir, nonHidden);
212 for (FileStatus store : storeDirs) {
213 LOG.debug("Deleting store for test");
214 fs.delete(store.getPath(), true);
215 }
216
217
218 HFileArchiver.archiveRegion(UTIL.getConfiguration(), fs, region.getRegionInfo());
219
220
221 assertFalse("Region directory (" + regionDir + "), still exists.", fs.exists(regionDir));
222
223 UTIL.deleteTable(TABLE_NAME);
224 }
225
226 @Test
227 public void testArchiveOnTableDelete() throws Exception {
228 TableName TABLE_NAME =
229 TableName.valueOf("testArchiveOnTableDelete");
230 UTIL.createTable(TABLE_NAME, TEST_FAM);
231
232 List<HRegion> servingRegions = UTIL.getHBaseCluster().getRegions(TABLE_NAME);
233
234 assertEquals(1, servingRegions.size());
235 Region region = servingRegions.get(0);
236
237
238 HRegionServer hrs = UTIL.getRSForFirstRegionInTable(TABLE_NAME);
239 FileSystem fs = hrs.getFileSystem();
240
241
242 LOG.debug("-------Loading table");
243 UTIL.loadRegion(region, TEST_FAM);
244
245
246 List<Region> regions = hrs.getOnlineRegions(TABLE_NAME);
247 assertEquals("More that 1 region for test table.", 1, regions.size());
248
249 region = regions.get(0);
250
251 region.waitForFlushesAndCompactions();
252
253
254 UTIL.getHBaseAdmin().disableTable(TABLE_NAME);
255 LOG.debug("Disabled table");
256
257
258 clearArchiveDirectory();
259
260
261 byte[][]columns = region.getTableDesc().getFamiliesKeys().toArray(new byte[0][]);
262 List<String> storeFiles = region.getStoreFileList(columns);
263
264
265 UTIL.deleteTable(TABLE_NAME);
266 LOG.debug("Deleted table");
267
268 assertArchiveFiles(fs, storeFiles, 30000);
269 }
270
271 private void assertArchiveFiles(FileSystem fs, List<String> storeFiles, long timeout) throws IOException {
272 long end = System.currentTimeMillis() + timeout;
273 Path archiveDir = HFileArchiveUtil.getArchivePath(UTIL.getConfiguration());
274 List<String> archivedFiles = new ArrayList<String>();
275
276
277
278 while (System.currentTimeMillis() < end) {
279 archivedFiles = getAllFileNames(fs, archiveDir);
280 if (archivedFiles.size() >= storeFiles.size()) {
281 break;
282 }
283 }
284
285 Collections.sort(storeFiles);
286 Collections.sort(archivedFiles);
287
288 LOG.debug("Store files:");
289 for (int i = 0; i < storeFiles.size(); i++) {
290 LOG.debug(i + " - " + storeFiles.get(i));
291 }
292 LOG.debug("Archive files:");
293 for (int i = 0; i < archivedFiles.size(); i++) {
294 LOG.debug(i + " - " + archivedFiles.get(i));
295 }
296
297 assertTrue("Archived files are missing some of the store files!",
298 archivedFiles.containsAll(storeFiles));
299 }
300
301
302
303
304
305
306 @Test
307 public void testArchiveOnTableFamilyDelete() throws Exception {
308 TableName TABLE_NAME =
309 TableName.valueOf("testArchiveOnTableFamilyDelete");
310 UTIL.createTable(TABLE_NAME, new byte[][] {TEST_FAM, Bytes.toBytes("fam2")});
311
312 List<HRegion> servingRegions = UTIL.getHBaseCluster().getRegions(TABLE_NAME);
313
314 assertEquals(1, servingRegions.size());
315 Region region = servingRegions.get(0);
316
317
318 HRegionServer hrs = UTIL.getRSForFirstRegionInTable(TABLE_NAME);
319 FileSystem fs = hrs.getFileSystem();
320
321
322 LOG.debug("-------Loading table");
323 UTIL.loadRegion(region, TEST_FAM);
324
325
326 List<Region> regions = hrs.getOnlineRegions(TABLE_NAME);
327 assertEquals("More that 1 region for test table.", 1, regions.size());
328
329 region = regions.get(0);
330
331 region.waitForFlushesAndCompactions();
332
333
334 UTIL.getHBaseAdmin().disableTable(TABLE_NAME);
335 LOG.debug("Disabled table");
336
337
338 clearArchiveDirectory();
339
340
341 byte[][]columns = region.getTableDesc().getFamiliesKeys().toArray(new byte[0][]);
342 List<String> storeFiles = region.getStoreFileList(columns);
343
344
345 UTIL.getHBaseAdmin().deleteColumn(TABLE_NAME, TEST_FAM);
346
347 assertArchiveFiles(fs, storeFiles, 30000);
348
349 UTIL.deleteTable(TABLE_NAME);
350 }
351
352
353
354
355 @Test
356 public void testCleaningRace() throws Exception {
357 final long TEST_TIME = 20 * 1000;
358 final ChoreService choreService = new ChoreService("TEST_SERVER_NAME");
359
360 Configuration conf = UTIL.getMiniHBaseCluster().getMaster().getConfiguration();
361 Path rootDir = UTIL.getDataTestDirOnTestFS("testCleaningRace");
362 FileSystem fs = UTIL.getTestFileSystem();
363
364 Path archiveDir = new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY);
365 Path regionDir = new Path(FSUtils.getTableDir(new Path("./"),
366 TableName.valueOf("table")), "abcdef");
367 Path familyDir = new Path(regionDir, "cf");
368
369 Path sourceRegionDir = new Path(rootDir, regionDir);
370 fs.mkdirs(sourceRegionDir);
371
372 Stoppable stoppable = new StoppableImplementation();
373
374
375 HFileCleaner cleaner = getHFileCleaner(stoppable, conf, fs, archiveDir);
376 try {
377 choreService.scheduleChore(cleaner);
378
379
380 long startTime = System.currentTimeMillis();
381 for (long fid = 0; (System.currentTimeMillis() - startTime) < TEST_TIME; ++fid) {
382 Path file = new Path(familyDir, String.valueOf(fid));
383 Path sourceFile = new Path(rootDir, file);
384 Path archiveFile = new Path(archiveDir, file);
385
386 fs.createNewFile(sourceFile);
387
388 try {
389
390 HFileArchiver.archiveRegion(fs, rootDir,
391 sourceRegionDir.getParent(), sourceRegionDir);
392
393
394
395 LOG.debug("hfile=" + fid + " should be in the archive");
396 assertTrue(fs.exists(archiveFile));
397 assertFalse(fs.exists(sourceFile));
398 } catch (IOException e) {
399
400
401
402 LOG.debug("hfile=" + fid + " should be in the source location");
403 assertFalse(fs.exists(archiveFile));
404 assertTrue(fs.exists(sourceFile));
405
406
407 fs.delete(sourceFile, false);
408 }
409 }
410 } finally {
411 stoppable.stop("test end");
412 cleaner.cancel(true);
413 choreService.shutdown();
414 fs.delete(rootDir, true);
415 }
416 }
417
418
419 private HFileCleaner getHFileCleaner(Stoppable stoppable, Configuration conf, FileSystem fs,
420 Path archiveDir) throws IOException {
421 Map<String, Object> params = new HashMap<>();
422 params.put(HMaster.MASTER, UTIL.getMiniHBaseCluster().getMaster());
423 HFileCleaner cleaner = new HFileCleaner(1, stoppable, conf, fs, archiveDir, POOL);
424 return Objects.requireNonNull(cleaner);
425 }
426
427 private void clearArchiveDirectory() throws IOException {
428 UTIL.getTestFileSystem().delete(
429 new Path(UTIL.getDefaultRootDirPath(), HConstants.HFILE_ARCHIVE_DIRECTORY), true);
430 }
431
432
433
434
435
436
437
438
439 private List<String> getAllFileNames(final FileSystem fs, Path archiveDir) throws IOException {
440 FileStatus[] files = FSUtils.listStatus(fs, archiveDir, new PathFilter() {
441 @Override
442 public boolean accept(Path p) {
443 if (p.getName().contains(HConstants.RECOVERED_EDITS_DIR)) {
444 return false;
445 }
446 return true;
447 }
448 });
449 return recurseOnFiles(fs, files, new ArrayList<String>());
450 }
451
452
453 private List<String> recurseOnFiles(FileSystem fs, FileStatus[] files, List<String> fileNames)
454 throws IOException {
455 if (files == null || files.length == 0) return fileNames;
456
457 for (FileStatus file : files) {
458 if (file.isDirectory()) {
459 recurseOnFiles(fs, FSUtils.listStatus(fs, file.getPath(), null), fileNames);
460 } else fileNames.add(file.getPath().getName());
461 }
462 return fileNames;
463 }
464 }