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