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