1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.snapshot;
20
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertTrue;
23
24 import java.io.IOException;
25 import java.net.URI;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Set;
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.FSDataOutputStream;
36 import org.apache.hadoop.fs.FileStatus;
37 import org.apache.hadoop.fs.FileSystem;
38 import org.apache.hadoop.fs.FileUtil;
39 import org.apache.hadoop.fs.Path;
40 import org.apache.hadoop.hbase.HBaseTestingUtility;
41 import org.apache.hadoop.hbase.HColumnDescriptor;
42 import org.apache.hadoop.hbase.HConstants;
43 import org.apache.hadoop.hbase.HRegionInfo;
44 import org.apache.hadoop.hbase.HTableDescriptor;
45 import org.apache.hadoop.hbase.MediumTests;
46 import org.apache.hadoop.hbase.TableName;
47 import org.apache.hadoop.hbase.client.HBaseAdmin;
48 import org.apache.hadoop.hbase.client.HTable;
49 import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
50 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
51 import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
52 import org.apache.hadoop.hbase.util.Bytes;
53 import org.apache.hadoop.hbase.util.FSUtils;
54 import org.apache.hadoop.hbase.util.Pair;
55 import org.junit.After;
56 import org.junit.AfterClass;
57 import org.junit.Before;
58 import org.junit.BeforeClass;
59 import org.junit.Test;
60 import org.junit.experimental.categories.Category;
61
62
63
64
65 @Category(MediumTests.class)
66 public class TestExportSnapshot {
67 private final Log LOG = LogFactory.getLog(getClass());
68
69 protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
70
71 private final static byte[] FAMILY = Bytes.toBytes("cf");
72
73 private byte[] emptySnapshotName;
74 private byte[] snapshotName;
75 private TableName tableName;
76 private HBaseAdmin admin;
77
78 public static void setUpBaseConf(Configuration conf) {
79 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
80 conf.setInt("hbase.regionserver.msginterval", 100);
81 conf.setInt("hbase.client.pause", 250);
82 conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 6);
83 conf.setBoolean("hbase.master.enabletable.roundrobin", true);
84 conf.setInt("mapreduce.map.max.attempts", 10);
85 conf.setInt("mapred.map.max.attempts", 10);
86 }
87
88 @BeforeClass
89 public static void setUpBeforeClass() throws Exception {
90 setUpBaseConf(TEST_UTIL.getConfiguration());
91 TEST_UTIL.startMiniCluster(3);
92 TEST_UTIL.startMiniMapReduceCluster();
93 }
94
95 @AfterClass
96 public static void tearDownAfterClass() throws Exception {
97 TEST_UTIL.shutdownMiniMapReduceCluster();
98 TEST_UTIL.shutdownMiniCluster();
99 }
100
101
102
103
104 @Before
105 public void setUp() throws Exception {
106 this.admin = TEST_UTIL.getHBaseAdmin();
107
108 long tid = System.currentTimeMillis();
109 tableName = TableName.valueOf("testtb-" + tid);
110 snapshotName = Bytes.toBytes("snaptb0-" + tid);
111 emptySnapshotName = Bytes.toBytes("emptySnaptb0-" + tid);
112
113
114 SnapshotTestingUtils.createTable(TEST_UTIL, tableName, FAMILY);
115
116
117 admin.snapshot(emptySnapshotName, tableName);
118
119
120 HTable table = new HTable(TEST_UTIL.getConfiguration(), tableName);
121 SnapshotTestingUtils.loadData(TEST_UTIL, tableName, 500, FAMILY);
122
123
124 admin.snapshot(snapshotName, tableName);
125 }
126
127 @After
128 public void tearDown() throws Exception {
129 TEST_UTIL.deleteTable(tableName);
130 SnapshotTestingUtils.deleteAllSnapshots(TEST_UTIL.getHBaseAdmin());
131 SnapshotTestingUtils.deleteArchiveDirectory(TEST_UTIL);
132 }
133
134
135
136
137
138
139
140
141
142
143
144 @Test
145 public void testBalanceSplit() throws Exception {
146
147 List<Pair<Path, Long>> files = new ArrayList<Pair<Path, Long>>();
148 for (long i = 0; i <= 20; i++) {
149 files.add(new Pair<Path, Long>(new Path("file-" + i), i));
150 }
151
152
153
154
155
156
157
158 List<List<Path>> splits = ExportSnapshot.getBalancedSplits(files, 5);
159 assertEquals(5, splits.size());
160 assertEquals(Arrays.asList(new Path("file-20"), new Path("file-11"),
161 new Path("file-10"), new Path("file-1"), new Path("file-0")), splits.get(0));
162 assertEquals(Arrays.asList(new Path("file-19"), new Path("file-12"),
163 new Path("file-9"), new Path("file-2")), splits.get(1));
164 assertEquals(Arrays.asList(new Path("file-18"), new Path("file-13"),
165 new Path("file-8"), new Path("file-3")), splits.get(2));
166 assertEquals(Arrays.asList(new Path("file-17"), new Path("file-14"),
167 new Path("file-7"), new Path("file-4")), splits.get(3));
168 assertEquals(Arrays.asList(new Path("file-16"), new Path("file-15"),
169 new Path("file-6"), new Path("file-5")), splits.get(4));
170 }
171
172
173
174
175 @Test
176 public void testExportFileSystemState() throws Exception {
177 testExportFileSystemState(tableName, snapshotName, snapshotName, 2);
178 }
179
180 @Test
181 public void testExportFileSystemStateWithSkipTmp() throws Exception {
182 TEST_UTIL.getConfiguration().setBoolean(ExportSnapshot.CONF_SKIP_TMP, true);
183 testExportFileSystemState(tableName, snapshotName, snapshotName, 2);
184 }
185
186 @Test
187 public void testEmptyExportFileSystemState() throws Exception {
188 testExportFileSystemState(tableName, emptySnapshotName, emptySnapshotName, 1);
189 }
190
191 @Test
192 public void testConsecutiveExports() throws Exception {
193 Path copyDir = getLocalDestinationDir();
194 testExportFileSystemState(tableName, snapshotName, snapshotName, 2, copyDir, false);
195 testExportFileSystemState(tableName, snapshotName, snapshotName, 2, copyDir, true);
196 removeExportDir(copyDir);
197 }
198
199 @Test
200 public void testExportWithTargetName() throws Exception {
201 final byte[] targetName = Bytes.toBytes("testExportWithTargetName");
202 testExportFileSystemState(tableName, snapshotName, targetName, 2);
203 }
204
205
206
207
208
209 @Test
210 public void testSnapshotWithRefsExportFileSystemState() throws Exception {
211 Configuration conf = TEST_UTIL.getConfiguration();
212
213 final TableName tableWithRefsName =
214 TableName.valueOf("tableWithRefs");
215 final String snapshotName = "tableWithRefs";
216 final String TEST_FAMILY = Bytes.toString(FAMILY);
217 final String TEST_HFILE = "abc";
218
219 final SnapshotDescription sd = SnapshotDescription.newBuilder()
220 .setName(snapshotName)
221 .setTable(tableWithRefsName.getNameAsString()).build();
222
223 FileSystem fs = TEST_UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getFileSystem();
224 Path rootDir = TEST_UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();
225 Path archiveDir = new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY);
226
227
228 HRegionInfo hri = new HRegionInfo(tableWithRefsName);
229 HRegionFileSystem r0fs = HRegionFileSystem.createRegionOnFileSystem(conf,
230 fs, FSUtils.getTableDir(archiveDir, hri.getTable()), hri);
231 Path storeFile = new Path(rootDir, TEST_HFILE);
232 FSDataOutputStream out = fs.create(storeFile);
233 out.write(Bytes.toBytes("Test Data"));
234 out.close();
235 r0fs.commitStoreFile(TEST_FAMILY, storeFile);
236
237
238
239 hri = new HRegionInfo(tableWithRefsName);
240 HRegionFileSystem r1fs = HRegionFileSystem.createRegionOnFileSystem(conf,
241 fs, new Path(archiveDir, hri.getTable().getNameAsString()), hri);
242 storeFile = new Path(rootDir, TEST_HFILE + '.' + r0fs.getRegionInfo().getEncodedName());
243 out = fs.create(storeFile);
244 out.write(Bytes.toBytes("Test Data"));
245 out.close();
246 r1fs.commitStoreFile(TEST_FAMILY, storeFile);
247
248 Path tableDir = FSUtils.getTableDir(archiveDir, tableWithRefsName);
249 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);
250 FileUtil.copy(fs, tableDir, fs, snapshotDir, false, conf);
251 SnapshotDescriptionUtils.writeSnapshotInfo(sd, snapshotDir, fs);
252
253 byte[] name = Bytes.toBytes(snapshotName);
254 testExportFileSystemState(tableWithRefsName, name, name, 2);
255 }
256
257 private void testExportFileSystemState(final TableName tableName, final byte[] snapshotName,
258 final byte[] targetName, int filesExpected) throws Exception {
259 Path copyDir = getHdfsDestinationDir();
260 testExportFileSystemState(tableName, snapshotName, targetName, filesExpected, copyDir, false);
261 removeExportDir(copyDir);
262 }
263
264
265
266
267 private void testExportFileSystemState(final TableName tableName, final byte[] snapshotName,
268 final byte[] targetName, int filesExpected, Path copyDir, boolean overwrite)
269 throws Exception {
270 URI hdfsUri = FileSystem.get(TEST_UTIL.getConfiguration()).getUri();
271 FileSystem fs = FileSystem.get(copyDir.toUri(), new Configuration());
272 copyDir = copyDir.makeQualified(fs);
273
274 List<String> opts = new ArrayList<String>();
275 opts.add("-snapshot");
276 opts.add(Bytes.toString(snapshotName));
277 opts.add("-copy-to");
278 opts.add(copyDir.toString());
279 if (targetName != snapshotName) {
280 opts.add("-target");
281 opts.add(Bytes.toString(targetName));
282 }
283 if (overwrite) opts.add("-overwrite");
284
285
286 int res = ExportSnapshot.innerMain(TEST_UTIL.getConfiguration(),
287 opts.toArray(new String[opts.size()]));
288 assertEquals(0, res);
289
290
291 FileStatus[] rootFiles = fs.listStatus(copyDir);
292 assertEquals(filesExpected, rootFiles.length);
293 for (FileStatus fileStatus: rootFiles) {
294 String name = fileStatus.getPath().getName();
295 assertTrue(fileStatus.isDir());
296 assertTrue(name.equals(HConstants.SNAPSHOT_DIR_NAME) ||
297 name.equals(HConstants.HFILE_ARCHIVE_DIRECTORY));
298 }
299
300
301 final FileSystem hdfs = FileSystem.get(hdfsUri, TEST_UTIL.getConfiguration());
302 final Path snapshotDir = new Path(HConstants.SNAPSHOT_DIR_NAME, Bytes.toString(snapshotName));
303 final Path targetDir = new Path(HConstants.SNAPSHOT_DIR_NAME, Bytes.toString(targetName));
304 verifySnapshot(hdfs, new Path(TEST_UTIL.getDefaultRootDirPath(), snapshotDir),
305 fs, new Path(copyDir, targetDir));
306 verifyArchive(fs, copyDir, tableName, Bytes.toString(targetName));
307 FSUtils.logFileSystemState(hdfs, snapshotDir, LOG);
308 }
309
310
311
312
313 @Test
314 public void testExportFailure() throws Exception {
315 assertEquals(1, runExportAndInjectFailures(snapshotName, false));
316 }
317
318
319
320
321 @Test
322 public void testExportRetry() throws Exception {
323 assertEquals(0, runExportAndInjectFailures(snapshotName, true));
324 }
325
326
327
328
329 private int runExportAndInjectFailures(final byte[] snapshotName, boolean retry)
330 throws Exception {
331 Path copyDir = getLocalDestinationDir();
332 URI hdfsUri = FileSystem.get(TEST_UTIL.getConfiguration()).getUri();
333 FileSystem fs = FileSystem.get(copyDir.toUri(), new Configuration());
334 copyDir = copyDir.makeQualified(fs);
335
336 Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
337 conf.setBoolean(ExportSnapshot.CONF_TEST_FAILURE, true);
338 conf.setBoolean(ExportSnapshot.CONF_TEST_RETRY, retry);
339
340
341 Path sourceDir = TEST_UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();
342 int res = ExportSnapshot.innerMain(conf, new String[] {
343 "-snapshot", Bytes.toString(snapshotName),
344 "-copy-from", sourceDir.toString(),
345 "-copy-to", copyDir.toString()
346 });
347 return res;
348 }
349
350
351
352
353 private void verifySnapshot(final FileSystem fs1, final Path root1,
354 final FileSystem fs2, final Path root2) throws IOException {
355 Set<String> s = new HashSet<String>();
356 assertEquals(listFiles(fs1, root1, root1), listFiles(fs2, root2, root2));
357 }
358
359
360
361
362 private void verifyArchive(final FileSystem fs, final Path rootDir,
363 final TableName tableName, final String snapshotName) throws IOException {
364 final Path exportedSnapshot = new Path(rootDir,
365 new Path(HConstants.SNAPSHOT_DIR_NAME, snapshotName));
366 final Path exportedArchive = new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY);
367 LOG.debug(listFiles(fs, exportedArchive, exportedArchive));
368 SnapshotReferenceUtil.visitReferencedFiles(fs, exportedSnapshot,
369 new SnapshotReferenceUtil.FileVisitor() {
370 public void storeFile (final String region, final String family, final String hfile)
371 throws IOException {
372 verifyNonEmptyFile(new Path(exportedArchive,
373 new Path(FSUtils.getTableDir(new Path("./"), tableName),
374 new Path(region, new Path(family, hfile)))));
375 }
376
377 public void recoveredEdits (final String region, final String logfile)
378 throws IOException {
379 verifyNonEmptyFile(new Path(exportedSnapshot,
380 new Path(tableName.getNameAsString(), new Path(region, logfile))));
381 }
382
383 public void logFile (final String server, final String logfile)
384 throws IOException {
385 verifyNonEmptyFile(new Path(exportedSnapshot, new Path(server, logfile)));
386 }
387
388 private void verifyNonEmptyFile(final Path path) throws IOException {
389 assertTrue(path + " should exists", fs.exists(path));
390 assertTrue(path + " should not be empty", fs.getFileStatus(path).getLen() > 0);
391 }
392 });
393
394
395 SnapshotDescription desc = SnapshotDescriptionUtils.readSnapshotInfo(fs, exportedSnapshot);
396 assertTrue(desc.getName().equals(snapshotName));
397 assertTrue(desc.getTable().equals(tableName.getNameAsString()));
398 }
399
400 private Set<String> listFiles(final FileSystem fs, final Path root, final Path dir)
401 throws IOException {
402 Set<String> files = new HashSet<String>();
403 int rootPrefix = root.toString().length();
404 FileStatus[] list = FSUtils.listStatus(fs, dir);
405 if (list != null) {
406 for (FileStatus fstat: list) {
407 LOG.debug(fstat.getPath());
408 if (fstat.isDir()) {
409 files.addAll(listFiles(fs, root, fstat.getPath()));
410 } else {
411 files.add(fstat.getPath().toString().substring(rootPrefix));
412 }
413 }
414 }
415 return files;
416 }
417
418 private Path getHdfsDestinationDir() {
419 Path rootDir = TEST_UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();
420 Path path = new Path(new Path(rootDir, "export-test"), "export-" + System.currentTimeMillis());
421 LOG.info("HDFS export destination path: " + path);
422 return path;
423 }
424
425 private Path getLocalDestinationDir() {
426 Path path = TEST_UTIL.getDataTestDir("local-export-" + System.currentTimeMillis());
427 LOG.info("Local export destination path: " + path);
428 return path;
429 }
430
431 private void removeExportDir(final Path path) throws IOException {
432 FileSystem fs = FileSystem.get(path.toUri(), new Configuration());
433 FSUtils.logFileSystemState(fs, path, LOG);
434 fs.delete(path, true);
435 }
436 }