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.TableName;
41 import org.apache.hadoop.hbase.client.Admin;
42 import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
43 import org.apache.hadoop.hbase.mob.MobConstants;
44 import org.apache.hadoop.hbase.mob.MobUtils;
45 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
46 import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos.SnapshotFileInfo;
47 import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;
48 import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils.SnapshotMock;
49 import org.apache.hadoop.hbase.testclassification.MediumTests;
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 TestMobExportSnapshot {
65 private final Log LOG = LogFactory.getLog(getClass());
66
67 protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
68
69 private final static byte[] FAMILY = Bytes.toBytes("cf");
70
71 private byte[] emptySnapshotName;
72 private byte[] snapshotName;
73 private int tableNumFiles;
74 private TableName tableName;
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(MobConstants.MOB_FILE_CACHE_SIZE_KEY, 0);
85 conf.setInt("hfile.format.version", 3);
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 MobSnapshotTestingUtils.createMobTable(TEST_UTIL, tableName, 1, FAMILY);
115
116
117 admin.snapshot(emptySnapshotName, tableName);
118
119
120 SnapshotTestingUtils.loadData(TEST_UTIL, tableName, 50, FAMILY);
121 tableNumFiles = admin.getTableRegions(tableName).size();
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 @Test
144 public void testBalanceSplit() throws Exception {
145
146 List<Pair<SnapshotFileInfo, Long>> files = new ArrayList<Pair<SnapshotFileInfo, Long>>();
147 for (long i = 0; i <= 20; i++) {
148 SnapshotFileInfo fileInfo = SnapshotFileInfo.newBuilder()
149 .setType(SnapshotFileInfo.Type.HFILE)
150 .setHfile("file-" + i)
151 .build();
152 files.add(new Pair<SnapshotFileInfo, Long>(fileInfo, i));
153 }
154
155
156
157
158
159
160
161 List<List<Pair<SnapshotFileInfo, Long>>> splits = ExportSnapshot.getBalancedSplits(files, 5);
162 assertEquals(5, splits.size());
163
164 String[] split0 = new String[] {"file-20", "file-11", "file-10", "file-1", "file-0"};
165 verifyBalanceSplit(splits.get(0), split0, 42);
166 String[] split1 = new String[] {"file-19", "file-12", "file-9", "file-2"};
167 verifyBalanceSplit(splits.get(1), split1, 42);
168 String[] split2 = new String[] {"file-18", "file-13", "file-8", "file-3"};
169 verifyBalanceSplit(splits.get(2), split2, 42);
170 String[] split3 = new String[] {"file-17", "file-14", "file-7", "file-4"};
171 verifyBalanceSplit(splits.get(3), split3, 42);
172 String[] split4 = new String[] {"file-16", "file-15", "file-6", "file-5"};
173 verifyBalanceSplit(splits.get(4), split4, 42);
174 }
175
176 private void verifyBalanceSplit(final List<Pair<SnapshotFileInfo, Long>> split,
177 final String[] expected, final long expectedSize) {
178 assertEquals(expected.length, split.size());
179 long totalSize = 0;
180 for (int i = 0; i < expected.length; ++i) {
181 Pair<SnapshotFileInfo, Long> fileInfo = split.get(i);
182 assertEquals(expected[i], fileInfo.getFirst().getHfile());
183 totalSize += fileInfo.getSecond();
184 }
185 assertEquals(expectedSize, totalSize);
186 }
187
188
189
190
191 @Test
192 public void testExportFileSystemState() throws Exception {
193 testExportFileSystemState(tableName, snapshotName, snapshotName, tableNumFiles);
194 }
195
196 @Test
197 public void testExportFileSystemStateWithSkipTmp() throws Exception {
198 TEST_UTIL.getConfiguration().setBoolean(ExportSnapshot.CONF_SKIP_TMP, true);
199 testExportFileSystemState(tableName, snapshotName, snapshotName, tableNumFiles);
200 }
201
202 @Test
203 public void testEmptyExportFileSystemState() throws Exception {
204 testExportFileSystemState(tableName, emptySnapshotName, emptySnapshotName, 0);
205 }
206
207 @Test
208 public void testConsecutiveExports() throws Exception {
209 Path copyDir = getLocalDestinationDir();
210 testExportFileSystemState(tableName, snapshotName, snapshotName, tableNumFiles, copyDir, false);
211 testExportFileSystemState(tableName, snapshotName, snapshotName, tableNumFiles, copyDir, true);
212 removeExportDir(copyDir);
213 }
214
215 @Test
216 public void testExportWithTargetName() throws Exception {
217 final byte[] targetName = Bytes.toBytes("testExportWithTargetName");
218 testExportFileSystemState(tableName, snapshotName, targetName, tableNumFiles);
219 }
220
221
222
223
224
225 @Test
226 public void testSnapshotWithRefsExportFileSystemState() throws Exception {
227 Configuration conf = TEST_UTIL.getConfiguration();
228
229 Path rootDir = TEST_UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();
230 FileSystem fs = TEST_UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getFileSystem();
231
232 SnapshotMock snapshotMock = new SnapshotMock(TEST_UTIL.getConfiguration(), fs, rootDir);
233 SnapshotMock.SnapshotBuilder builder =
234 snapshotMock.createSnapshotV2("tableWithRefsV1", "tableWithRefsV1");
235 testSnapshotWithRefsExportFileSystemState(builder);
236
237 snapshotMock = new SnapshotMock(TEST_UTIL.getConfiguration(), fs, rootDir);
238 builder = snapshotMock.createSnapshotV2("tableWithRefsV2", "tableWithRefsV2");
239 testSnapshotWithRefsExportFileSystemState(builder);
240 }
241
242
243
244
245
246 private void testSnapshotWithRefsExportFileSystemState(SnapshotMock.SnapshotBuilder builder)
247 throws Exception {
248 Path[] r1Files = builder.addRegion();
249 Path[] r2Files = builder.addRegion();
250 builder.commit();
251 int snapshotFilesCount = r1Files.length + r2Files.length;
252
253 byte[] snapshotName = Bytes.toBytes(builder.getSnapshotDescription().getName());
254 TableName tableName = builder.getTableDescriptor().getTableName();
255 testExportFileSystemState(tableName, snapshotName, snapshotName, snapshotFilesCount);
256 }
257
258 private void testExportFileSystemState(final TableName tableName, final byte[] snapshotName,
259 final byte[] targetName, int filesExpected) throws Exception {
260 Path copyDir = getHdfsDestinationDir();
261 testExportFileSystemState(tableName, snapshotName, targetName, filesExpected, copyDir, false);
262 removeExportDir(copyDir);
263 }
264
265
266
267
268 private void testExportFileSystemState(final TableName tableName, final byte[] snapshotName,
269 final byte[] targetName, int filesExpected, Path copyDir, boolean overwrite)
270 throws Exception {
271 URI hdfsUri = FileSystem.get(TEST_UTIL.getConfiguration()).getUri();
272 FileSystem fs = FileSystem.get(copyDir.toUri(), new Configuration());
273 copyDir = copyDir.makeQualified(fs);
274
275 List<String> opts = new ArrayList<String>();
276 opts.add("-snapshot");
277 opts.add(Bytes.toString(snapshotName));
278 opts.add("-copy-to");
279 opts.add(copyDir.toString());
280 if (targetName != snapshotName) {
281 opts.add("-target");
282 opts.add(Bytes.toString(targetName));
283 }
284 if (overwrite) opts.add("-overwrite");
285
286
287 int res = ExportSnapshot.innerMain(TEST_UTIL.getConfiguration(),
288 opts.toArray(new String[opts.size()]));
289 assertEquals(0, res);
290
291
292 FileStatus[] rootFiles = fs.listStatus(copyDir);
293 assertEquals(filesExpected > 0 ? 2 : 1, rootFiles.length);
294 for (FileStatus fileStatus: rootFiles) {
295 String name = fileStatus.getPath().getName();
296 assertTrue(fileStatus.isDirectory());
297 assertTrue(name.equals(HConstants.SNAPSHOT_DIR_NAME) ||
298 name.equals(HConstants.HFILE_ARCHIVE_DIRECTORY));
299 }
300
301
302 final FileSystem hdfs = FileSystem.get(hdfsUri, TEST_UTIL.getConfiguration());
303 final Path snapshotDir = new Path(HConstants.SNAPSHOT_DIR_NAME, Bytes.toString(snapshotName));
304 final Path targetDir = new Path(HConstants.SNAPSHOT_DIR_NAME, Bytes.toString(targetName));
305 verifySnapshotDir(hdfs, new Path(TEST_UTIL.getDefaultRootDirPath(), snapshotDir),
306 fs, new Path(copyDir, targetDir));
307 Set<String> snapshotFiles = verifySnapshot(fs, copyDir, tableName, Bytes.toString(targetName));
308 assertEquals(filesExpected, snapshotFiles.size());
309 }
310
311
312
313
314 @Test
315 public void testExportFailure() throws Exception {
316 assertEquals(1, runExportAndInjectFailures(snapshotName, false));
317 }
318
319
320
321
322 @Test
323 public void testExportRetry() throws Exception {
324 assertEquals(0, runExportAndInjectFailures(snapshotName, true));
325 }
326
327
328
329
330 private int runExportAndInjectFailures(final byte[] snapshotName, boolean retry)
331 throws Exception {
332 Path copyDir = getLocalDestinationDir();
333 URI hdfsUri = FileSystem.get(TEST_UTIL.getConfiguration()).getUri();
334 FileSystem fs = FileSystem.get(copyDir.toUri(), new Configuration());
335 copyDir = copyDir.makeQualified(fs);
336
337 Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
338 conf.setBoolean(ExportSnapshot.CONF_TEST_FAILURE, true);
339 conf.setBoolean(ExportSnapshot.CONF_TEST_RETRY, retry);
340
341
342 Path sourceDir = TEST_UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();
343 int res = ExportSnapshot.innerMain(conf, new String[] {
344 "-snapshot", Bytes.toString(snapshotName),
345 "-copy-from", sourceDir.toString(),
346 "-copy-to", copyDir.toString()
347 });
348 return res;
349 }
350
351
352
353
354 private void verifySnapshotDir(final FileSystem fs1, final Path root1,
355 final FileSystem fs2, final Path root2) throws IOException {
356 assertEquals(listFiles(fs1, root1, root1), listFiles(fs2, root2, root2));
357 }
358
359
360
361
362 private Set<String> verifySnapshot(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 Set<String> snapshotFiles = new HashSet<String>();
367 final Path exportedArchive = new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY);
368 SnapshotReferenceUtil.visitReferencedFiles(TEST_UTIL.getConfiguration(), fs, exportedSnapshot,
369 new SnapshotReferenceUtil.SnapshotVisitor() {
370 @Override
371 public void storeFile(final HRegionInfo regionInfo, final String family,
372 final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
373 if(MobUtils.isMobRegionInfo(regionInfo))
374 return;
375 String hfile = storeFile.getName();
376 snapshotFiles.add(hfile);
377 if (storeFile.hasReference()) {
378
379 } else {
380 verifyNonEmptyFile(new Path(exportedArchive,
381 new Path(FSUtils.getTableDir(new Path("./"), tableName),
382 new Path(regionInfo.getEncodedName(), new Path(family, hfile)))));
383 }
384 }
385
386 @Override
387 public void logFile (final String server, final String logfile)
388 throws IOException {
389 snapshotFiles.add(logfile);
390 verifyNonEmptyFile(new Path(exportedSnapshot, new Path(server, logfile)));
391 }
392
393 private void verifyNonEmptyFile(final Path path) throws IOException {
394 assertTrue(path + " should exists", fs.exists(path));
395 assertTrue(path + " should not be empty", fs.getFileStatus(path).getLen() > 0);
396 }
397 });
398
399
400 SnapshotDescription desc = SnapshotDescriptionUtils.readSnapshotInfo(fs, exportedSnapshot);
401 assertTrue(desc.getName().equals(snapshotName));
402 assertTrue(desc.getTable().equals(tableName.getNameAsString()));
403 return snapshotFiles;
404 }
405
406 private Set<String> listFiles(final FileSystem fs, final Path root, final Path dir)
407 throws IOException {
408 Set<String> files = new HashSet<String>();
409 int rootPrefix = root.toString().length();
410 FileStatus[] list = FSUtils.listStatus(fs, dir);
411 if (list != null) {
412 for (FileStatus fstat: list) {
413 LOG.debug(fstat.getPath());
414 if (fstat.isDirectory()) {
415 files.addAll(listFiles(fs, root, fstat.getPath()));
416 } else {
417 files.add(fstat.getPath().toString().substring(rootPrefix));
418 }
419 }
420 }
421 return files;
422 }
423
424 private Path getHdfsDestinationDir() {
425 Path rootDir = TEST_UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();
426 Path path = new Path(new Path(rootDir, "export-test"), "export-" + System.currentTimeMillis());
427 LOG.info("HDFS export destination path: " + path);
428 return path;
429 }
430
431 private Path getLocalDestinationDir() {
432 Path path = TEST_UTIL.getDataTestDir("local-export-" + System.currentTimeMillis());
433 LOG.info("Local export destination path: " + path);
434 return path;
435 }
436
437 private void removeExportDir(final Path path) throws IOException {
438 FileSystem fs = FileSystem.get(path.toUri(), new Configuration());
439 fs.delete(path, true);
440 }
441 }