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.CategoryBasedTimeout;
38 import org.apache.hadoop.hbase.HBaseTestingUtility;
39 import org.apache.hadoop.hbase.HConstants;
40 import org.apache.hadoop.hbase.HRegionInfo;
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.testclassification.MediumTests;
51 import org.apache.hadoop.hbase.util.Bytes;
52 import org.apache.hadoop.hbase.util.FSUtils;
53 import org.apache.hadoop.hbase.util.Pair;
54 import org.junit.After;
55 import org.junit.AfterClass;
56 import org.junit.Before;
57 import org.junit.BeforeClass;
58 import org.junit.Rule;
59 import org.junit.Test;
60 import org.junit.experimental.categories.Category;
61 import org.junit.rules.TestRule;
62
63
64
65
66 @Category(MediumTests.class)
67 public class TestExportSnapshot {
68 @Rule public final TestRule timeout = CategoryBasedTimeout.builder().
69 withTimeout(this.getClass()).withLookingForStuckThread(true).build();
70 private static final Log LOG = LogFactory.getLog(TestExportSnapshot.class);
71
72 protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
73
74 private final static byte[] FAMILY = Bytes.toBytes("cf");
75
76 private byte[] emptySnapshotName;
77 private byte[] snapshotName;
78 private int tableNumFiles;
79 private TableName tableName;
80 private Admin admin;
81
82 public static void setUpBaseConf(Configuration conf) {
83 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
84 conf.setInt("hbase.regionserver.msginterval", 100);
85 conf.setInt("hbase.client.pause", 250);
86 conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 6);
87 conf.setBoolean("hbase.master.enabletable.roundrobin", true);
88 conf.setInt("mapreduce.map.maxattempts", 10);
89 }
90
91 @BeforeClass
92 public static void setUpBeforeClass() throws Exception {
93 setUpBaseConf(TEST_UTIL.getConfiguration());
94 TEST_UTIL.startMiniCluster(3);
95 TEST_UTIL.startMiniMapReduceCluster();
96 }
97
98 @AfterClass
99 public static void tearDownAfterClass() throws Exception {
100 TEST_UTIL.shutdownMiniMapReduceCluster();
101 TEST_UTIL.shutdownMiniCluster();
102 }
103
104
105
106
107 @Before
108 public void setUp() throws Exception {
109 this.admin = TEST_UTIL.getHBaseAdmin();
110
111 long tid = System.currentTimeMillis();
112 tableName = TableName.valueOf("testtb-" + tid);
113 snapshotName = Bytes.toBytes("snaptb0-" + tid);
114 emptySnapshotName = Bytes.toBytes("emptySnaptb0-" + tid);
115
116
117 SnapshotTestingUtils.createTable(TEST_UTIL, tableName, FAMILY);
118
119
120 admin.snapshot(emptySnapshotName, tableName);
121
122
123 Table table = new HTable(TEST_UTIL.getConfiguration(), tableName);
124 SnapshotTestingUtils.loadData(TEST_UTIL, tableName, 50, FAMILY);
125 tableNumFiles = admin.getTableRegions(tableName).size();
126
127
128 admin.snapshot(snapshotName, tableName);
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
365
366
367 private Set<String> verifySnapshot(final FileSystem fs, final Path rootDir,
368 final TableName tableName, final String snapshotName) throws IOException {
369 final Path exportedSnapshot = new Path(rootDir,
370 new Path(HConstants.SNAPSHOT_DIR_NAME, snapshotName));
371 final Set<String> snapshotFiles = new HashSet<String>();
372 final Path exportedArchive = new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY);
373 SnapshotReferenceUtil.visitReferencedFiles(TEST_UTIL.getConfiguration(), fs, exportedSnapshot,
374 new SnapshotReferenceUtil.SnapshotVisitor() {
375 @Override
376 public void storeFile(final HRegionInfo regionInfo, final String family,
377 final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
378 String hfile = storeFile.getName();
379 snapshotFiles.add(hfile);
380 if (storeFile.hasReference()) {
381
382 } else {
383 verifyNonEmptyFile(new Path(exportedArchive,
384 new Path(FSUtils.getTableDir(new Path("./"), tableName),
385 new Path(regionInfo.getEncodedName(), new Path(family, hfile)))));
386 }
387 }
388
389 @Override
390 public void logFile (final String server, final String logfile)
391 throws IOException {
392 snapshotFiles.add(logfile);
393 verifyNonEmptyFile(new Path(exportedSnapshot, new Path(server, logfile)));
394 }
395
396 private void verifyNonEmptyFile(final Path path) throws IOException {
397 assertTrue(path + " should exists", fs.exists(path));
398 assertTrue(path + " should not be empty", fs.getFileStatus(path).getLen() > 0);
399 }
400 });
401
402
403 SnapshotDescription desc = SnapshotDescriptionUtils.readSnapshotInfo(fs, exportedSnapshot);
404 assertTrue(desc.getName().equals(snapshotName));
405 assertTrue(desc.getTable().equals(tableName.getNameAsString()));
406 return snapshotFiles;
407 }
408
409 private Set<String> listFiles(final FileSystem fs, final Path root, final Path dir)
410 throws IOException {
411 Set<String> files = new HashSet<String>();
412 int rootPrefix = root.toString().length();
413 FileStatus[] list = FSUtils.listStatus(fs, dir);
414 if (list != null) {
415 for (FileStatus fstat: list) {
416 LOG.debug(fstat.getPath());
417 if (fstat.isDirectory()) {
418 files.addAll(listFiles(fs, root, fstat.getPath()));
419 } else {
420 files.add(fstat.getPath().toString().substring(rootPrefix));
421 }
422 }
423 }
424 return files;
425 }
426
427 private Path getHdfsDestinationDir() {
428 Path rootDir = TEST_UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();
429 Path path = new Path(new Path(rootDir, "export-test"), "export-" + System.currentTimeMillis());
430 LOG.info("HDFS export destination path: " + path);
431 return path;
432 }
433
434 private Path getLocalDestinationDir() {
435 Path path = TEST_UTIL.getDataTestDir("local-export-" + System.currentTimeMillis());
436 LOG.info("Local export destination path: " + path);
437 return path;
438 }
439
440 private void removeExportDir(final Path path) throws IOException {
441 FileSystem fs = FileSystem.get(path.toUri(), new Configuration());
442 fs.delete(path, true);
443 }
444 }